结构是一种值类型,通常用来封装小型相关变量组。
结构可以包含构造函数、常量、字段、方法、属性、索引器、运算符、事件和嵌套类型。
例子
public struct Person
{
private int iD;
public string Name;
public int ID
{
get { return iD; }
set { iD = value; }
}
public string GetName()
{
return Name;
}
}
private void button1_Click(object sender, EventArgs e)
{
Person person = new Person();
person.Name = "小明";
MessageBox.Show(person.GetName());
}
结构与类的不同
1.类型不同
结构是值类型,值类型的内存地址系统自动分配。栈。
类是引用类型,需要申请。比如用 new 关键字。堆。
栈的执行效率要比堆的执行效率高,但是栈的资源有限,不适合处理大的复杂的对象。
结构不能给字段初始值。如:public struct Person { public int ID = 0; } 是不行的。
2.继承性
结构无法继承另一个结构,但可以继承接口。
结构常用于存储数据,如表示点的坐标,点的颜色等,在处理一些逻辑算法时都用类。
直接选择排序
直接选择排序是一种选择排序,是一种不稳定的排序,时间复杂度为 O(n^2)。
基本思想
每一趟从待排序的数据元素中选出最小的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
40 10 20 15
①{10} 40 20 15 将第一个元素 40 和无序区中最小的 10 交换
②{10 15} 20 40 将第二个元素 40 和无序区中最小的 15 交换
③{10 15 20} 40 将第三个元素 20 已是无序区中最小不用交换
④{10 15 20 40} 到倒数第二个元素结束
例子
using System;
using System.Collections;
namespace System
{
public class Test
{
public static void Main()
{
int[] array = new int[6] { 40, 10, 20, 15, 35, 20 };
SelectionSort.Sort(array);
PrintArray(array);
Console.ReadKey();
}
public static void PrintArray(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
if (i > 0) Console.Write(" ");
Console.Write(array[i].ToString());
}
Console.WriteLine();
}
}
public class SelectionSort
{
public static void Sort(int[] sortArray)
{
// 从起始元素开始选到倒数第二个元素
for (int i = 0; i < sortArray.Length - 1; i++)
{
int min = i;
for (int j = i + 1; j < sortArray.Length; j++) // 循环无序区,找到最小元素的下标 min
{
if (sortArray[j] < sortArray[min])
{
min = j;
}
}
// 将从无序区选出的最小元素和当前待处理的有序区交换
if (i != min)
{
Swap(ref sortArray[i], ref sortArray[min]);
}
}
}
private static void Swap(ref int a, ref int b)
{
int t;
t = a;
a = b;
b = t;
}
}
}
堆排序
堆排序是一种选择排序。是不稳定的排序方法。时间复杂度为O(nlog2n)。
堆排序的特点是:在排序过程中,将排序数组看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。
基本思想
1.将要排序的数组创建为一个大根堆。大根堆的堆顶元素就是这个堆中最大的元素。
2.将大根堆的堆顶元素和无序区最后一个元素交换,并将无序区最后一个位置例入有序区,然后将新的无序区调整为大根堆。
重复操作,无序区在递减,有序区在递增。
初始时,整个数组为无序区,第一次交换后无序区减一,有序区增一。
每一次交换,都是大根堆的堆顶元素插入有序区,所以有序区保持是有序的。
大根堆和小根堆
堆:是一颗完全二叉树。
大根堆:所有节点的子节点比其自身小的堆
小根堆:所有节点的子节点比其自身大的堆
完全二叉树的基本性质
数组中有n个元素,i是节点
1 <= i <= n/2 就是说数组的后一半元素都是叶子节点。
i的父节点位置:i/2
i左子节点位置:i*2
i右子节点位置:i*2 + 1
如一个数组
物理结构:15 20 35 25 30 40 50
逻辑结构:
15
/
20 35
/ /
25 30 40 50
这是一个小根堆,所有节点的子节点比其自身大。15,20,35是节点,其它的都是叶子。
由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
例子
using System;
using System.Collections;
namespace System
{
public class Test
{
public static void Main()
{
int[] array = new int[6] { 40, 10, 20, 15, 35, 20 };
HeapSort.Sort(array);
PrintArray(array);
Console.ReadKey();
}
public static void PrintArray(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
if (i > 0) Console.Write(" ");
Console.Write(array[i].ToString());
}
Console.WriteLine();
}
}
public class HeapSort
{
public static void Sort(int[] sortArray)
{
BuildMaxHeap(sortArray);
for (int i = (sortArray.Length - 1); i > 0; i--)
{
Swap(ref sortArray[0], ref sortArray[i]); // 将堆顶元素和无序区的最后一个元素交换
MaxHeapify(sortArray, 0, i); // 将新的无序区调整为堆,无序区在变小
}
}
/// <summary>
/// 初始大根堆,自底向上地建堆
/// 完全二叉树的基本性质,最底层节点是 n/2,所以从 sortArray.Length / 2 开始
/// </summary>
private static void BuildMaxHeap(int[] sortArray)
{
for (int i = (sortArray.Length / 2) - 1; i >= 0; i--)
{
MaxHeapify(sortArray, i, sortArray.Length);
}
}
/// <summary>
/// 将指定的节点调整为堆
/// </summary>
/// <param name="i">需要调整的节点</param>
/// <param name="heapSize">堆的大小,也指数组中无序区的长度</param>
private static void MaxHeapify(int[] sortArray, int i, int heapSize)
{
int left = 2 * i + 1; // 左子节点
int right = 2 * i + 2; // 右子节点
int larger = i; // 临时变量,存放大的节点值
// 比较左子节点
if (left < heapSize && sortArray[left] > sortArray[larger])
{
larger = left;
}
// 比较右子节点
if (right < heapSize && sortArray[right] > sortArray[larger])
{
larger = right;
}
// 如有子节点大于自身就交换,使大的元素上移。
if (i != larger)
{
Swap(ref sortArray[i], ref sortArray[larger]);
MaxHeapify(sortArray, larger, heapSize);
}
}
private static void Swap(ref int a, ref int b)
{
int t;
t = a;
a = b;
b = t;
}
}
}
ComboBox 是一个下拉列表框。
DataSource
获取或设置此 ComboBox 的数据源。
DisplayMember
获取或设置要为此 ListControl 显示的属性。(从 ListControl 继承。)
ValueMember
获取或设置一个属性,该属性将用作 ListControl 中的项的实际值。(从 ListControl 继承。)
SelectedIndex
获取或设置指定当前选定项的索引。
SelectedText
获取或设置 ComboBox 的可编辑部分中选定的文本。
SelectedItem
获取或设置 ComboBox 中当前选定的项。
ArrayList list = new ArrayList();
list.Add(new Info());
list.Add(new Info());
comboBox1.DataSource = list;
comboBox1.DisplayMember = "Name";
public class Info
{
private string name = string.Empty;
public string Name
{
get{return name;}
set{name = value;}
}
}
comboBox1.ValueMember 好象没用
实现的方法
public class ListItem
{
private string key;
private string value;
public ListItem(string key, string value)
{
this.key = key;
this.value = value;
}
public string Key
{
get { return this.key; }
set { this.key = value; }
}
public string Value
{
get { return this.value; }
set { this.value = value; }
}
// 关键点,重写方法
public override string ToString()
{
return this.value;
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
// 返回 ListItem.Value 的值
textBoxLayout.Text = comboBox1.SelectedItem.ToString();
}
comboBox1.Items.Add(new ListItem("AAA","1"));
comboBox1.Items.Add(new ListItem("BBB", "2"));
comboBox1.DisplayMember = "Key";
类级
private void button1_Click(object sender, EventArgs e)
{
Filler<Info> filer = new Filler<Info>();
Info info = new Info();
info.Name = "A";
filer.Add(info);
}
public class Filler<T>
{
IList<T> list = new List<T>();
public void Add(T t)
{
list.Add(t);
}
}
public class Info
{
private string name = "";
public string Name { get { return name; } set { name = value; } }
}
方法级
private void button1_Click(object sender, EventArgs e)
{
Filler filer = new Filler();
Info info = new Info();
info.Name = "A";
filer.Add<Info>(info);
}
public class Filler
{
ArrayList list = new ArrayList();
public void Add<T>(T t)
{
list.Add(t);
}
}
public class Info
{
private string name = "";
public string Name { get { return name; } set { name = value; } }
}
希尔排序是插入排序的一种,时间性能优于直接插入排序,是一种不稳定的排序,时间复杂度为 O(nlogn)。
基本思想
将整个无序列分割成若干小的子序列分别进行直接插入排序。
先取一个小于 n 的整数 d1 作为第一个增量,把文件的全部记录分成 d1 个组。所有距离为 dl 的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量 d2 < d1 重复上述的分组和排序,直至所取的增量 dt = 1 (dt < dt-l < … <d2 < d1),即所有记录放在同一组中进行直接插入排序为止。
最后一个增量必须为 1。
当第一个增量为 1 时,就等于直接插入排序。
如下:
增量:2,分为 2 组
30 20 40 15 35 25
30 -- 40 ---35 <- 第一组
20 -- 15 -- 25 <- 第二组
每组进行直接插入排序。
40 和 30 比较,顺序正确(第一组的有序段:30 40)
15 和 20 比较,顺序错误,20 后移,已经是最前面了,15 插入原 20 的位置(第二组的有序段:15 20)
35 和 40 比较,顺序错误,40 后移,40 前面还有数,所以还要继续比较
35 和 30 比较,顺序正确,35 找到了插入点(第一组的有序段:30 35 40)
25 和 20 比较,顺序正确(第二组的有序段:15 20 25)
到这里,第一趟结束。
第一组:30 35 40
第二组:15 20 25
30 15 35 20 40 25
例子
using System;
namespace System
{
public class Test
{
public static void Main()
{
int[] array = new int[6] { 30, 20, 40, 15, 35, 25 };
PrintArray(array);
Console.WriteLine("-----------------");
ShellSort.Sort(array);
PrintArray(array);
Console.ReadKey();
}
public static void PrintArray(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
if (i > 0) Console.Write(" ");
Console.Write(array[i].ToString());
}
Console.WriteLine();
}
}
public class ShellSort
{
public static void Sort(int[] array)
{
int increment = array.Length;
do
{
increment = increment / 3 + 1; // 设置增量
Sort(array, increment);
} while (increment > 1);
}
public static void Sort(int[] array, int increment)
{
// 进行直接插入排序
for (int i = increment; i < array.Length; i++) // 从第二个开始
{
if (array[i] < array[i - increment]) // 前面有大的数,需要后移
{
int j = i - increment; // j 表示前面数的指针
int temp = array[i];
do
{
array[j + increment] = array[j]; // 后移元素
j = j - increment;
} while (j >= 0 && array[j] > temp); // 前面的数还是大的时,继续循环后移
array[j + increment] = temp; // array[i] 插入正确的位置上
}
}
}
}
}
直接插入排序是一种最简单的插入排序法,是一种稳定排序,时间复杂度为 O(n^2)。
基本思想
每次从无序段中取出第一个元素,把它插入到有序段的合适位置,使有序段仍然有序。
这样无序段在变短,有序段在变长,直到无序段变没了。
开始时,有序段只有一个元素就是第一个元素,其它都为无序段。
如下:
大括号中的是有序段。
{30} 20 40 15 35 25
① 20 和 30 比较,30 后移一位
{20 30} 40 15 35 25
② 40 和 30 比较,不用移动
{20 30 40} 15 35 25
③ 15 和 40 比较,40 后移一位,继续比较下一位
④ 15 和 30 比较,30 后移一位,继续比较下一位
⑤ 15 和 20 比较,20 后移一位
{15 20 30 40} 35 25
⑥ 35 和 40 比较,40 后移一位,继续比较下一位
⑦ 35 和 30 比较,不用移动
{15 20 30 35 40} 25
⑧ 25 和 40 比较,40 后移一位,继续比较下一位
⑨ 25 和 35 比较,35 后移一位,继续比较下一位
.....
{15 20 25 30 35 40}
例子
using System;
namespace System
{
public class Test
{
public static void Main()
{
int[] array = new int[6] { 30, 20, 40, 15, 35, 25 };
PrintArray(array);
Console.WriteLine("-----------------");
StraightSort.Sort(array);
PrintArray(array);
Console.ReadKey();
}
public static void PrintArray(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
if (i > 0) Console.Write(" ");
Console.Write(array[i].ToString());
}
Console.WriteLine();
}
}
public class StraightSort
{
public static void Sort(int[] array)
{
for (int i = 1; i < array.Length; i++) // 从第二个开始
{
if (array[i] < array[i - 1]) // 前面有大的数,需要后移
{
int j = i - 1; // j 表示前面数的指针
int temp = array[i];
do
{
array[j + 1] = array[j]; // 后移元素
j = j - 1;
} while (j >= 0 && array[j] > temp);// 前面的数还是大的时,继续循环后移
array[j + 1] = temp; // array[i] 插入正确的位置上
}
}
}
}
}
1.基本概念
1.1 稳定排序和不稳定排序
稳定排序是所有相等的数经过某种排序方法后,仍能保持它们在排序之前的相对次序。反之,就是不稳定排序。
比如:一组数字排序前是 a1,a2,a3,a4 其中 a2 和 a3 相等,经过某种排序后为 a4,a2,a3,a1 则称这种排序是稳定的,因为 a2 排序前在 a3 的前面,排序后还是在 a3 的前面。如果变成 a4,a3,a2,a1 就是不稳定的。
1.2 内排序和外排序
在排序过程中,所有需要排序的数都在内存,并在内存中调整它们的存储顺序,称为内排序;在排序过程中,只有部分数被调入内存,完成后再从外存调入数据进行排序,称为外排序,适用于数据很大不能一次调入内存。
内排序的常用算法有:冒泡排序、选择排序、插入排序、堆排序、快速排序
1.3 算法的时间复杂度和空间复杂度
1) 时间复杂度
一个问题的规模是n,解这一问题的某一算法所重复执行的次数为T(n),当n不断变化时,T(n)也会不断变化。但有时想知道它变化时呈现什么规律。为此,引入时间复杂度概念。
一般情况下,算法中操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n))为算法的渐进时间复杂度,简称时间复杂度。
记作: T(n) = O(f(n))
以冒泡排序算法为例:
冒泡排序算法重复执行的次数是:1 + 2 + 3 ... + (n-1) = n * (n-1) * 1/2
n * (n-1) * 1/2 <= n * n * 1/2
T(n) = n * (n-1) * 1/2
f(n) = n * n
当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数
T(n)/f(n) = 1/2
记作: T(n) = O(n*n)
冒泡排序算法的时间复杂度为 O(n*n)。
2) 空间复杂度
与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。
记作: S(n) = O(f(n))
2.常见的排序算法
2.1 冒泡排序
2.2 选择排序
2.3 插入排序
2.4 堆排序
2.5 归并排序
2.6 快速排序
2.7 希尔排序
委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。
委托方法的使用可以像其他任何方法一样,具有参数和返回值。
一个最简单的例子。
using System;
namespace DelegateTest
{
// 用 delegate 关键字定义一个委托。
public delegate void WriteText(string message);
public class Test
{
public static void Main()
{
WriteText handler = Write; // 实例化委托
// 可以用“+”或“+=”给委托添加方法或删除方法
// 委托和它绑定的方法它们的参数和返回值要一样,否则是不能绑定的。
handler += Write2;
// 使用可以像其他任何方法一样,具有参数和返回值。
handler("Hello World!");
Console.ReadKey();
}
public static void Write(string message)
{
System.Console.WriteLine(String.Format("*** {0} ***", message));
}
public static void Write2(string message)
{
System.Console.WriteLine(String.Format("### {0} ###", message));
}
}
}