冒泡排序:
假设有个数组 int [] array = {12,5,24,78,123};
两个for循环嵌套。
外层循环控制比较趟数:
先拿出来第一个数 12 和其他数比较,所以控制比较次数为数组长度-1,也就是4次(12和5,24,78,123比);
内层循环是:
将 第一个数 12 与剩下的数比较,循环内部加一个if判断语句,如果 第一个数 12 大于第二个数 5 ,那么这两个数就交换位置(显然12>5,交换位置);
接下来第二个数(12和5已经交换了位置,那么第二个数就是12) 12 与第三个数 24 比较,如果第二个数大于第三个数就交换位置(12<24,不交换位置);剩下的以此类推......
第一趟比较结束,已经比出来了数组中最大的数,所以内存循环中应该减去一次比较,第二趟循环次数应该为:数组长度-1-已经比较的次数(也就是控制外层循环的变量);
写出来是:
for(int i = 0;i < array.Length - 1;i++){
for(int j = 0;j < array.Length - i - 1;j++){
if(array[j]>array[j+1]){
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
如果想稍微进行下优化:可以定义一个bool值变量,如果进入排序发现某一趟中,所有数值的顺序已经完全排好了,没有进入这个if判断语句,可以直接用break直接跳出循环。
选择排序:
选出一个最大或最小的放在数组首或尾,选好之后选剩余数组中数值最大的或最小的。
这次来倒序排序;
还是两个for循环嵌套,第一层for循环和冒泡排序一样,是限制比较趟数,也是数组长度减一。
定义两个变量,分别存储每一趟比较的最大值,和最大值下标
先假设第一个数值是最大的,存储他的值和下标;(int Max = array[i] ; int maxIndex = i; )
接着写第二个for循环,
要拿第一个数跟第二个数比较,所以第二个for循环的起始值应该定义为 int j = i+1;
在起始值已经控制了,所以 j< 数组长度就可以了。
第二个第二个for循环内部加个if判断语句,然后拿数组的第一个数跟剩下所有的值比,比较出一个最大的值(if (array[j]>max)) 每次取最小值和保存下标,该层for循环结束后就找出了剩余数的最小值和下标,记录下最大值的下标和最大值(Max = array [j];maxIndex = j),在第二个for循环外面,将最大值给当前i 下标的数值,将当前i 下标的值给最大值下标的数值,也就是交换(将当前i下标的值跟最小值进行交换 array[maxIndex] = array[j];array[j] = max)。
写出来是:
for (int i = 0; i < array.Length-1; i++) {
int max = array [i];
int maxIndex = i;
for (int j = i + 1; j < array.Length; j++) {
if (array[j]>max) {
max = array [j];
maxIndex = j;
}
}
array [maxIndex] = array [i];
array [i] = max;
}
二维数组:
可以理解为存储着多个一维数组的数组
元素数组类型[,]数组名 =new 元素数组类型[,]{
{ele01,ele02,ele03}
{ele11,ele12,ele13}
{ele21,ele22,ele23}
动态初始化加new,静态直接赋值。
两个for循环遍历二维数组,外层for循环控制行,内层for循环控制列。
GetLength(n) 获取第n维的长度 0 - 行 1- 列
foreach(变量类型 变量名 in 数组名){ } foreach 也叫快速枚举
string str = "shu170221"; str[2] = u ;通过字符串加下标可以访问到其中某个字符;
枚举:
具有一组命名常量的独特的值类型,用于声明一组命名的常数。
格式: enum 枚举名{
枚举值1,枚举值2,枚举值3......
}
创建枚举变量格式:枚举类型 变量名 = 枚举类型.变量名
枚举值默认和整型数字相关联,从0开始;
枚举名.GetHashCode() 获取枚举值对应的哈希值
枚举名.GetTypeCode() 获取关联的数值类型 int32等
枚举名.GetType()获取该变量所属的枚举类型
类:
具有相同特征和行为的抽象;
类的定义关键字class
class 类型{
1.字段;用来描述类的特征
2.属性;用来限制字段的可读可写等(get set 方法)
3.方法;用来描述类的行为
方法格式:访问修饰符 + 返回值类型(没有返回值就写void)+方法名(参数列表){ 方法体;(有返回值需要return)}
根据返回值类型和参数列表方法可分为4种:
1.无参无返;2.无参有返;3.有参无返;4.有参有返
调用方法的格式:实例对象.方法名(参数);
}
对象:类的具体实例
对象的创建:类名+变量名 = new 类名();
面向对象 - OOP Object oriented Programming
构造方法:
当我们创建实例对象时使用
默认构造:类名+()+{ }
如果没有自定义构造方法 类名+(),就是调用了该类的默认构造方法
注意:1.方法名跟类名相同 2.不用写返回值类型 3.不用return
构造方法主要使用创建对象进行初始化
当我们给系统添加了自定义的构造方法之后想要使用系统默认的构造方法必须手动提供默认构造
方法重载:
在一个类里面存在两个或两个以上 方法名相同 但是 参数类型 不同叫做方法的重载
重载的需求:1.方法名必须相同
2.参数必须不同(不同分两种情况 1.参数列表数量不同 2.参数类型不同)
重载跟返回值类型无关;
自定义构造方法:
格式与默认构造相同 类名+(参数列表)+{ }
this 关键字:this在方法里面表示调用该方法的实例对象的引用
面向对象:
特征:继承;封装;多态
封装:将特定功能的代码使用方法存储起来,并提供对外访问的接口
封装:提高代码的复用,方便代码的阅读,对问题的定位更加方便和准确
继承:当类之间存在共性,继承关系的时候就使用继承
A类继承B类 格式classA:B(被继承的类在后,相当于你爸爸一直在你身后站着)
被继承的类叫做基类(父类) 继承了基类的类叫做派生类(子类)
派生类继承基类的所有公共成员变量(字段,属性,方法)
类的继承只能单继承,也就是说只能继承一个类,c不支持多继承(一个儿子一个爹)
自定义构造方法:
派生类默认会调用基类的默认构造
派生类如果需要调用基类有参数的构造方法(基类的自定义构造)base关键字
需要 访问修饰符+类名+(派生类新参数):base(基类参数-这些参数可以不加类型名){
};
虚方法(重写):
virtual 和 override 关键字
基类里面有些方法需要在派生类里面重新写的就将基类的方法使用virtual修饰,称为虚方法
派生类重写该方法使用override关键字修饰
重写了之后基类调用的是基类原来的方法,派生类调用的是重写后的方法
派生类调用基类的方法使用base关键字 base.方法名();
完全重写:就是删掉base.方法名 重写的东西就是完全重写
隐藏方法(覆盖):
基类和派生类中存在签名相同(返回值 方法名 参数列表 都相同)的方法,并且基类和派生类中的方法没有用virtual和override修饰,那么就是隐藏方法,也就是覆盖。
派生类里面的方法用new关键字修饰
覆盖和重写的区别:
覆盖(隐藏方法)之后基类的方法不存在了,只剩下派生类里的方法
重写之后基类和派生类的方法都还在
堆区栈区:
栈区空间小,读取速度快 特点:先进后出 由计算机虚拟内存自动进行释放管理。
用于分配值类型。当值类型不在其作用域时,其所占的内存空间中释放,栈的执行效率为最高的。
堆区空间大,读取和存储速度慢,堆中的内存空间是自由分配的,一般存储引用类型的数据,内存空间大。
值类型 存储在栈中 只需一段内存存储 在栈中开辟空间后存储
引用类型 存储在堆中 需要两端内存存储 一段在栈中存储数据-在堆中的地址
一段在堆中开辟空间存储实际数据
结构体:
自定义的一种数据类型,可以存放多种数据类型
格式:struct 结构体类型名{ 字段,属性,方法 };
在结构体的构造方法中 必须给所有的字段赋值;
结构体的默认构造不允许写,因为系统已经给你提供了;
结构体变量访问属性一定要先创建初始化;(先new一个对象 然后在访问属性)
在结构体构造方法里面不允许通过属性给私有字段赋值(构造方法初始化中必须访问字段,不能访问属性)
结构体没有析构函数
结构体不能被继承
和类一样,可以拥有字段、属性、方法
结构体可以直接初始化常量结构体成员变量
四大修饰符:
public 访问不受限制 公共的;
private 该修饰符修饰的成员只能在类内部或结构体内部使用,在子类或者外部访问不到
protected 该修饰符修饰的成员只能在类或子类的内部可以访问,外部不可访问
internal 当前程序集可以访问(同一命名空间)
类只有两种访问修饰符:public 和 internal 默认是internal
方法参数:
实参形参
通俗的说,形参就是函数定义时参数表的各个变量,实参就是调用函数时给函数的变量(写在函数名后的圆括号里)
值参数和引用参数的本质:
值参数是对栈中数据进行拷贝,拷贝出来的数据相当于之前数据的副本。
引用类型是对栈中数据地址的引用,当值参数为某对象引用时,可以改变该对象的某些值,但是不能将值编程新对象的地址。
引用参数:
用 ref 修饰符声明的参数是引用参数。
引用参数不创建新的存储位置。相反,引用参数表示的是那个在对该函数成员调用中被当作“自变量”的变量所表示的同一个存储位置。因此,引用参数的值总是与基础变量相同。
ref关键字
当值类型想要达到引用类型的效果,使用ref关键字
在方法的定义和调用的时候都要使用ref 关键字
使用ref关键字修饰的变量必须先初始化
当使用ref参数时,则方法的定义和调用都要显示使用ref关键字
传递到ref参数的参数必须先进行初始化
输出参数
如果想要一个方法返回多个值,可以使用输出参数来处理。
out关键字
通过引用传递参数;
方法定义和调用都要使用out关键字 实例:
public static void Calculate(int value1,int value2,out int sum,out int cha){
sum = value1 + value2;
cha = value1 - value2;
}
int sum,cha;
Calculate (30, 20,out sum,out cha);
Console.WriteLine ("sum = {0},\ncha = {1}",sum,cha);
数组参数
如果形参表中包含了数组型参数,那么他必须在参数表中位于最后,而且必须是一位数组。
使用params 修饰的数组,在调用方法的时候可以不写数组名
直接将数组当中所有元素直接写上(可以是0个)
params 只能使用一次,而且必须放在最后面
格式:params 数组类型 [ ] 数组名
C#中方法的参数有四种类型:
值参数:不添加任何修饰符
输出参数:以out修饰符声明,可以返回一个或多个值给调用者
引用参数:以ref修饰符声明
数组参数:以params修饰符声明