说是复习,与其说是学习更加的准确,毕竟大部分的思想都是忘记了,而且书有弄丢了,所以在网上东拼西凑的找了一下资料,来学习排序算法,
毕竟这个是非常基础而且重要的东西,大部分是使用C#来实现的,有用List的,也有用IList的,总之主要的目的是想了解这些算法背后的编程思想,
这个是非常重要的,毕竟 语言知识工具,算法才是程序的灵魂
1 选择排序:
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)。
思想:
n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果:
①初始状态:无序区为R[1..n],有序区为空。
②第1趟排序
在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。
③第i趟排序
第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。
通俗的解释
对比数组中前一个元素跟后一个元素的大小,如果后面的元素比前面的元素小则用一个变量k来记住他的位置,接着第二次比较,前面“后一个元素”现变成了“前一个元素”,继续跟他的“后一个元素”进行比较如果后面的元素比他要小则用变量k记住它在数组中的位置(下标),等到循环结束的时候,我们应该找到了最小的那个数的下标了,然后进行判断,如果这个元素的下标不是第一个元素的下标,就让第一个元素跟他交换一下值,这样就找到整个数组中最小的数了。然后找到数组中第二小的数,让他跟数组中第二个元素交换一下值,以此类推。
/// <summary>
/// 选择排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="arr"></param>
static void selection_sort<T>(T[] arr) where T : System.IComparable<T>
{
int i, j, min, len = arr.Length;
T temp;
for (i = 0; i < len - 1; i++)
{
min = i;
for (j = i + 1; j < len; j++)
{
if (arr[min].CompareTo(arr[j]) > 0)
{
min = j;
}
}
temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
二分法插入排序是在插入第i个元素时,对前面的0~i-1元素进行折半,先跟他们中间的那个元素比,如果小,则对前半再进行折半,否则对后半进行折半,直到left>right,然后再把第i个元素前1位与目标位置之间的所有元素后移,再把第i个元素放在目标位置上。
/// <summary>
/// 二分法插入排序
/// </summary>
/// <param name="data"></param>
static void InsertSort(int[] data)
{
int i, j;
int count = data.Length;
for (i = 1; i < count; i++)
{
int t = data[i];
for (j = i - 1; j > 0 && data[i] > t; j--)
{
data[j + 1] = data[j];
}
data[j + 1] = t;
}
}
public static void InsertSortImprovedWithBinarySearch(IList<int> data)
{
int temp;
int tempIndex;
for (int i = 1; i < data.Count; i++)
{
temp = data[i];
tempIndex = BinarySearchForInsertSort(data, 0, i, i);
for (int j = i - 1; j >= tempIndex; j--)
{
data[j + 1] = data[j];
}
data[tempIndex] = temp;
}
}
public static int BinarySearchForInsertSort(IList<int> data, int low, int high, int key)
{
if (low >= data.Count - 1)
return data.Count - 1;
if (high <= 0)
return 0;
int mid = (low + high) / 2;
if (mid == key) return mid;
if (data[key] > data[mid])
{
if (data[key] < data[mid + 1])
return mid + 1;
return BinarySearchForInsertSort(data, mid + 1, high, key);
}
else
{
if (mid - 1 < 0) return 0;
if (data[key] > data[mid - 1])
return mid;
return BinarySearchForInsertSort(data, low, mid - 1, key);
}
}
3。快速排序:
设要排序的
数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的
排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,
排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给
key,即
key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于
key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于
key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于
key,4中A[i]不大于
key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
public int UnitSort(int[] array, int low, int hight)
{
int key = array[low];
while(low < hight)
{
while (low < hight && array[hight] > key) hight--;
array[low] = array[hight];
while (low < hight && array[low] <= key) low++;
array[hight] = array[low];
}
array[low] = key;
return hight;
}
public void Sort(int[] array, int low, int high)
{
if (low >= high) return;
int index = UnitSort(array, low, high);
SSort(array, low, index - 1);
SSort(array, index + 1, high);
}
4。归并排序:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路
归并。
归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。
算法描述
编辑
归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经
排序序列之和,该空间用来存放合并后的序列
第二步:设定两个
指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
public static List<int> Sort(List<int> list)
{
if (list.Count <= 1)
return list;
int mid = list.Count / 2;
List<int> left = new List<int>();
List<int> right = new List<int>();
for (int i = 0; i < mid; i++)
{
left.Add(list[i]);
}
for (int i = mid; i < list.Count; i++)
{
right.Add(list[i]);
}
left = Sort(left);
right = Sort(right);
return Merge(left, right);
}
private static List<int> Merge(List<int> left, List<int> right)
{
List<int> temp = new List<int>();
while (left.Count > 0 && right.Count > 0)
{
if (left[0] < right[0])
{
temp.Add(left[0]);
left.RemoveAt(0);
}
else
{
temp.Add(right[0]);
right.RemoveAt(0);
}
if (left.Count > 0)
{
for (int i = 0; i < left.Count; i++)
{
temp.Add(left[i]);
}
}
if (right.Count > 0)
{
for (int i = 0; i < right.Count; i++)
{
temp.Add(right[i]);
}
}
}
}
5.堆排序:
堆排序(Heapsort)是指利用堆积树(堆)这种
数据结构
所设计的一种
排序算法
,它是选择排序的一种。可以利用
数组
的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是
完全二叉树
。大根堆的要求是每个节点的值都不大于其父节点的值,即
A[PARENT[i]] >= A[i]。
在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
private static void Swap(IList<int> numbers, int i, int j)
{
int number = numbers[i];
numbers[i] = numbers[j];
numbers[j] = number;
}
public static void HeapSort(IList<int> data)
{
BuildMaxHeapify(data);
int j = data.Count;
for (int i = 0; i < j; )
{
Swap(data, i, --j);
if (j - 2 < 0)
{
break;
}
int k = 0;
while (true)
{
if (k > (j - 2) / 2) break;
else
{
int temp = k;
k = ReSortMaxBranch(data, k, 2 * k + 1, 2 * k + 2, j - 1);
if (temp == k) break;
}
}
}
}
public static void BuildMaxHeapify(IList<int> data)
{
for (int i = data.Count / 2 - 1; i >= 0; i--)
{
int temp = i;
temp = ReSortMaxBranch(data, i, 2 * i + 1, 2 * i + 2, data.Count - 1);
if (temp != i)
{
int k = i;
while (k != temp && temp <= data.Count / 2 - 1)
{
k = temp;
temp = ReSortMaxBranch(data, temp, 2 * temp + 1, 2 * temp + 2, data.Count);
}
}
}
}
public static int ReSortMaxBranch(IList<int> data, int maxIndex, int left, int right, int lastIndex)
{
int temp;
if (right > lastIndex) temp = left;
else
{
if (data[left] > data[right])
{
temp = left;
}
else
{
temp = right;
}
}
if (data[maxIndex] < data[temp])
{
Swap(data, maxIndex, temp);
}
else
{
temp = maxIndex;
}
return temp;
}
6. 希尔排序:
基本思想
编辑
先取一个小于n的整数d1作为第一个
增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行
直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量
=1(
<
…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。



该方法实质上是一种分组插入方法
比较相隔较远距离(称为
增量)的数,使得数移动时能跨过多个元素,则进行一次比
[2]
较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的
排序算法中实现了这一思想。算法先将要排序的一组数按某个
增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当
增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为
增量,以后每次减半,直到增量为1。
给定实例的shell排序的排序过程
假设待排序文件有10个记录,其关键字分别是:
49,38,65,97,76,13,27,49,55,04。
增量序列的取值依次为:
public static void ShellSort(IList<int> data)
{
int temp;
for (int gap = data.Count / 2; gap > 0; gap /= 2)
{
for (int i = dap; i < data.Count; i++)
{
temp = data[i];
for (int j = i - gap; j >= 0; j -= gap)
{
if (data[j] > temp)
{
data[j + gap] = data[j];
if (j == 0)
{
data[j] = temp;
break;
}
}
else
{
data[j + gap] = temp;
break;
}
}
}
}
}
7.桶排序:
public static void BucketSortOnlyUnitDigit(IList<int> data)
{
int[] indexCounter = new int[10];
for (int i = 0; i < data.Count; i++)
{
indexCounter[data[i]]++;
}
int[] indexBegin = new int[10];
for (int i = 1; i < 10; i++)
{
indexBegin[i] = indexBegin[i - 1] + indexBegin[i - 1];
}
IList<int> tempList = NewInstance(data, data.Count);
for (int i = 0; i < data.Count; i++)
{
int number = data[i];
tempList[indexBegin[number]++] = data[i];
}
}