C#实现(递归和非递归)高速排序和简单排序等一系列排序算法
本人由于近期工作用到了一些排序算法。就把几个简单的排序算法。想冒泡排序,选择排序,插入排序。奇偶排序和高速排序等整理了出来,代码用C#代码实现,而且通过了測试。希望能给大家提供參考。
1.冒泡排序
冒泡排序,是指计算机的一种排序算法,它的时间复杂度是O(n^2),尽管不及堆排序和高速排序时间复杂度为O(nlogn,底数为2)。可是有两个长处:1:编程复杂度低。非常easy实现;2 是具有稳定性,这里的稳定性是指源序列中同样元素的相对顺序仍然保持到排序后的顺序,而堆排序和高速排序都不具有稳定性。
基本概念
冒泡排序(BubbleSort)的基本概念:依次比較相邻两个数,小的在前,大的在后。在第一趟,首先比較第1个数和第2个数,小的放在前面。大的放在后面,然后比較第2个数和第3个数,小的在前,大的在后。如此继续。直到比較最后两个数,小的在前,大的在后,第一趟结束时,就把最大的数放在了最后。在第二趟,仍从第一对数開始比較(由于由于第2个数和第3个数的交换。使第1个数不再小于第2个数),将小数放前。大数放后,一直比較到倒数第二个数(倒数第一个数已经是最大的),第二趟结束,这样在倒数第二个位置得到一个新的最大数,如此循环下去,反复以上过程,直至终于完毕排序。
比方有一个数列10, 33, 2, 4, 55, 6, 12, 34, 456, 66, 43, 23, 65, 1, 345, 61, 76, 31, 43, 76
第一次排序后:10,2,4,33,6,12,34,55,66,43,23,65,1,345,61,76,31,43,76,456
第二次排序后:2,4,10,6,12,33,34,55,43,23,65,1,66,61,76,31,43,76,345,456
第三次排序后:2,4,6,10,12,33,34,43,23,55,1,65,61,66,31,43,76,76,345,456
第四次排序后:2,4,6,10,12,33,34,23,43,1,55,61,65,31,43,66,76,76,345,456
第五次排序后: 2,4,6,10,12,33,23,34,1,43,55,61,31,43,65,66,76,76,345,456
第六次排序后: 2,4,6,10,12,23,33,1,34,43,55,31,43,61,65,66,76,76,345,456
第七次排序后: 2,4,6,10,12,23,1,33,34,43,31,43,55,61,65,66,76,76,345,456
第八次排序后: 2,4,6,10,12,1,23,33,34,31,43,43,55,61,65,66,76,76,345,456
第九次排序后: 2,4,6,10,1,12,23,33,31,34,43,43,55,61,65,66,76,76,345,456
第十次排序后: 2,4,6,1,10,12,23,31,33,34,43,43,55,61,65,66,76,76,345,456
第十一次排序后:2,4,1,6,10,12,23,31,33,34,43,43,55,61,65,66,76,76,345,456
第十二次排序后:2,1,4,6,10,12,23,31,33,34,43,43,55,61,65,66,76,76,345,456
第十三次排序后: 1,2,4,6,10,12,23,31,33,34,43,43,55,61,65,66,76,76,345,456
这样经过13趟排序后这个序列就成为一个有序的数列。
详细实现代码为:
private static void BubbleSort(int[] R)
{
int len = R.Length;
bool flag = false;
for (int i = 0; i < len-1; i++)
{
flag = false;
for (int j = 0; j < len - i-1; j++)
{
if (R[j] > R[j + 1])
{
Swap(ref R[j], ref R[j + 1]);
flag = true;
}
}
if (!flag)
{
break;
}
}
}
private static void Swap(ref int left, ref int right)
{
int temp = left;
left = right;
right = temp;
}
2. 选择排序
每一趟从待排序的元素中选择最小的(最大的)一个元素,顺序放在已排好序的数列的最后,直到待排序的元素派完,选择排序是不稳定的排序。
基本概念
具有n元素的数列能够进行n-1趟直接选择排序得到有序结果,初始状态有序区为空。无序区为R[1...n]
第1趟排序在无序区R[1...n]选择keyword最小的元素R[k],将它与无序区的R[1] 进行交换,使R[1...1]和R[2...n]变为个添加为1新有序区。和无序区的元素个数减去1的新无序区。
第i趟排序開始时,当前有序区和无序区分别为R[1..i-1]和R(1≤i≤n-1)。该趟排序从当前无序区中选出keyword最小的记录 R[k],将它与无序区的第1个记录R交换。
使R[1..i]和R分别变 为记录个数添加1个的新有序区和记录个数降低1个的新无序区。 这样,n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果 .
对于数列 10,33,2,4,55,6,12,34,456,66,43,23,65,1,345, 61,76,31,43,76
第一次排序后:1,33,2,4,55,6,12,34,456,66,43,23,65,10,345,61,76,31,43,76
第二次排序后: 1,2,33,4,55,6,12,34,456,66,43,23,65,10,345,61,76,31,43,76
第三次排序后:1,2,4,33,55,6,12,34,456,66,43,23,65,10,345,61,76,31,43,76
第四次排序后:1,2,4,6,55,33,12,34,456,66,43,23,65,10,345,61,76,31,43,76
第五次排序后:1,2,4,6,10,33,12,34,456,66,43,23,65,55,345,61,76,31,43,76
第六次排序后: 1,2,4,6,10,12,33,34,456,66,43,23,65,55,345,61,76,31,43,76
第七次排序后: 1,2,4,6,10,12,23,34,456,66,43,33,65,55,345,61,76,31,43,76
第八次排序后: 1,2,4,6,10,12,23,31,456,66,43,33,65,55,345,71,76,34,43,76
第九次排序后: 1,2,4,6,10,12,23,31,33,66,43,456,65,55,345,71,76,34,43,76
第十次排序后: 1,2,4,6,10,12,23,31,33,34,43,456,65,55,345,71,76,66,43,76
第十一次排序后: 1,2,4,6,10,12,23,31,33,34,43,43,65,55,345,71,76,66,456,76
第十二次排序后: 1,2,4,6,10,12,23,31,33,34,43,43,55,65,345,71,76,66,456,76
第十三次排序后: 1,2,4,6,10,12,23,31,33,34,43,43,55,65,66,71,76,345,456,76
第十四次排序后: 1,2,4,6,10,12,23,31,33,34,43,43,55,65,66,71,76,76,456,345
第十五次排序后: 1,2,4,6,10,12,23,31,33,34,43,43,55,65,66,71,76,76,345,456
终于经过15次排序后成为有序的数列。
详细实现代码为:
private static void SelectSort(int[] R)
{
int len = R.Length;
int min = 0;
for (int i = 0; i < len - 1; i++)
{
min = i;
for (int j = i + 1; j < len - 1; j++)
{
if (R[min] > R[j])
{
min = j;
}
}
Swap(ref R[i], ref R[min]);
}
}
3. 插入排序
插入排序算法是一种稳定的算法,时间复杂度是O(n^2)。适用于少量数据的排序。插入算法的基本操作就是将一个数据插入到已经排好序的有序数据中。从而得到一个新的,个数加1的有序数据。
插入算法把要排序的数组分为两部分:第一部分包括了数组的全部元素,但将最后一个元素除外。而第二部分就仅仅包括这一个元素,在第一部分排序后,再把最后这个元素插入到此刻已是有序的一部分中。
基本思想
1:每次处理都是将无序序列的第一个元素和有序序列的元素从后面逐一比較,查到合适的插入位置,将该元素插入到有序序列的合适位置。
2:从有序R[1]和无序R[2...n]開始进行排序。
3:处理第i个元素时(i=2,3,…,n) ,数列{R1,R2,...Ri-1}都是有序的。而数列{Ri,Ri+1,...Rn}都是无序的,用Ri 与{R1,R2,...Ri-1}逐个比較。找到合适位置。将Ri插入,
4:反复第三部,共进行n-i次插入处理,数组全部有序
对于数列 10,33,2,4,55,6,12,34,456,66,43,23,65,1,345, 61,76,31,43,76
第一次排序后:10,33,2,4,55,12,34,456,66,43,23,65,1,345,61,76,31,43,76
第二次排序后: 2,10,33,4,55,12,34,456,66,43,23,65,1,345,61,76,31,43,76
第三次排序后: 2,4,10,33,55,12,34,456,66,43,23,65,1,345,61,76,31,43,76
第四次排序后: 2,4,10,33,55,12,34,456,66,43,23,65,1,345,61,76,31,43,76
第五次排序后: 2,3,10,12,33,55,34,456,66,43,23,65,1,345,61,76,31,43,76
第六次排序后: 2,3,10,12,33,34,55,456,66,43,23,65,1,345,61,76,31,43,76
第七次排序后: 2,3,10,12,33,34,55,456,66,43,23,65,1,345,61,76,31,43,76
第八次排序后: 2,3,10,12,33,34,55,66,456,43,23,65,1,345,61,76,31,43,76
第九次排序后: 2,3,10,12,33,34,43,55,66,456,23,65,1,345,61,76,31,43,76
第十次排序后: 2,3,10,12,23,33,34,43,55,66,456,65,1,345,61,76,31,43,76
第十一次排序后: 2,3,10,12,23,33,34,43,55,65,66,456,1,345,61,76,31,43,76
第十二次排序后: 1,2,3,10,12,23,33,34,43,55,65,66,456,345,61,76,31,43,76
第十三次排序后: 1,2,3,10,12,23,33,34,43,55,65,66,345,456,61,76,31,43,76
第十四次排序后: 1,2,3,10,12,23,33,34,43,55,61,65,66,345,456,76,31,43,76
第十五次排序后: 1,2,3,10,12,23,33,34,43,55,61,65,66,76,345,456,31,43,76
第十六次排序后: 1,2,3,10,12,23,31,33,34,43,55,61,65,66,76,345,456,43,76
第十七次排序后: 1,2,3,10,12,23,31,33,34,43,43,55,61,65,66,76,345,456,76
第十八次排序后: 1,2,3,10,12,23,31,33,34,43,43,55,61,65,66,76,76,345,456
共十八次排序后,数组才是全部有序的。
实现代码为:
private static void InsertSort(int[] R)
{
int len = R.Length;
int j = 0;
int temp = 0;
for (int i = 1; i < len; i++)
{
temp=R[i];
j=i-1;
while (j >= 0 && temp < R[j])
{
R[j + 1] = R[j];
j--;
}
R[j + 1] = temp;
}
}
4. 高速排序
高速排序是对冒泡排序的一种改进,它的基本思想是:通过一趟排序将要排序的数列分成独立的两个部分,当中一部分的全部数据都比后一部分的全部数据都要小,然后再按此方法对这两个部分分别进行高速排序,整个排序过程能够递归进行,以次达到整个数列变为有序。
高速排序不是稳定的排序。
算法过程
设要排序的数组为R[1...n],首先随意选取一个元素(通常选择第一个元素)作为关键元素。然后把全部比它小的都放在前面,全部比它大的都放在后面,这个过程成为一趟高速排序。
1. 设置两个变量 low ,high,排序開始的时候low=0,high=n-1;
2. 以第一个数组元素为重要数据key,即key = R[low];
3. 从high開始往前搜索,即由后開始向前搜索,high=high-1,找到第一个小于key的值R[high], 并与R[low] 交换,
4. 从low 開始向后搜索,即由前開始向后搜索,low=low+1,找到第一个大于key的值R[low],并于R[high]交换。
5. 反复3,4.直到low=high.
对于数列 10,33,2,4,55,6,12,34,456,66,43,23,65,1,345, 61,76,31,43,76
初始重要数据key=10;
第一次交换后:1, 33, 2, 4, 55, 6, 12, 34, 456, 66, 43, 23, 65, 1, 345, 61, 76, 31, 43, 76
第二次交换后: 1, 33, 2, 4, 55, 6, 12, 34, 456, 66, 43, 23, 65, 33, 345, 61, 76, 31, 43, 76
第三次交换后: 1, 6, 2, 4, 55, 6, 12, 34, 456, 66, 43, 23, 65, 33, 345, 61, 76, 31, 43, 76
第四次交换后: 1, 6, 2, 4, 55, 55, 12, 34, 456, 66, 43, 23, 65, 33, 345, 61, 76, 31, 43, 76
这样low=high=4
再把R[low]=key
这样第一次高速排序后数列就变为{1,6,2,4}10{55,12,34,456,66,43,23,65,33,345,61,76,31,43,76}
这样再分别对{1,6,2,4}和{55,12,34,456,66,43,23,65,33,345,61,76,31,43,76} 分别进行高速排序,反复这个步骤,终于使整个数组都是有序的。
详细实现代码为:
private static void QuickSort(int[] R, int low, int high)
{
int pivotLoc = 0;
if(low<high)
{
pivotLoc = Partition(R, low, high);
QuickSort(R, low, pivotLoc - 1);
QuickSort(R, pivotLoc + 1, high);
}
}
private static int Partition(int[] R, int low, int high)
{
int temp = R[low];
while (low < high)
{
while (low < high && temp <= R[high])
{
high--;
}
R[low] = R[high];
while (low < high && temp >= R[low])
{
low++;
}
R[high] = R[low];
}
R[low] = temp;
return low;
}
//高速非递归排序
public static void QuickSort(int[] R, int Low, int High, Stack<int> stack)
{
int low = Low;
int high = High;
int temp = R[low];
while (high > low)
{
while (low < high && temp <= R[high])
{
high--;
}
if (high > low)
{
R[low] = R[high];
R[high] = temp;
}
while (low < high && temp >= R[low])
{
low++;
}
if (high > low)
{
R[high] = R[low];
R[low] = temp;
}
if (low == high)
{
if (Low < low - 1)
{
stack.Push(Low);
stack.Push(low - 1);
}
if (High > low + 1)
{
stack.Push(low + 1);
stack.Push(High);
}
}
}
}
測试代码:
static void Main(string[] args)
{
int[] arry = new int[] { 10, 33, 2, 4, 55, 6, 12, 34, 456, 66, 43, 23, 65, 1, 345, 61, 76, 31, 43, 76 };
Stack<int> s=new Stack<int>();
s.Push(0);
s.Push(arryk.Length - 1);
while (s.Count > 0)
{
int low = s.Pop();
int high = s.Pop();
if (low > high)
{
temp = low;
low = high;
high = temp;
}
QuickSort(arryk, low, high, s);
}
Console.ReadLine();
}
通过对10万条随机数据进行递归和非递归排序后发现,非递归方法的速度是递归方法速度的40倍左右。
5 . 一个无序的数组,怎样通过一个函数取出最大值和最小值,要求时间复杂度最低和空间最少
详细算法例如以下:
private static int[] GetMaxMin(int[] R)
{
int len=R.Length;
int min = R[0];
int max = R[0];
for (int i = 1; i < len;i++ )
{
if (min > R[i])
{
min = R[i];
}
if (max < R[i])
{
max = R[i];
}
}
int[] res = new int[2];
res[0] = min;
res[1] = max;
return res;
}
6 对于已知数组,随机存储一百个数。把奇数放左边。偶数放右边,详细算法例如以下:
private static void SortNumber(int[] R)
{
int high = R.Length - 1;
int low = 0;
int temp = R[low];
while (low < high)
{
while(low<high&&R[high]%2==0)
{
high--;
}
R[low] = R[high];
while (low < high && R[low] % 2 == 1)
{
low++;
}
R[high] = R[low];
}
R[low] = temp;
}
7.二分查找算法
二分查找算法又叫折半查找,长处是比較次数少,查找速度快。平均性能好,缺点是给定的数组必须是有序的,且插入删除困难,因此二分查找使用与不常常变动而又查找频繁的有序表。
首先,假设数组是依照升序的有序表。将表中间位置的元素与给定要查找的元素比較,假设相等。则查找成功。否则利用中间位置将表分为前后两个字表,假设中间位置记录的元素大于给定的查找的数据。在前面的字表中进行查找。否则在后面的字表中进行查找。反复以上过程,直到找到或没有子表为止。
详细实现代码例如以下:
private static int BinarySearch(int[] R, int arg)
{
int low = 0;
int high = R.Length - 1;
while (low < high)
{
int middle = (low + high) / 2;
if (arg == R[middle])
{
return middle;
}
else if (arg < R[middle])
{
high = middle - 1;
}
else
{
low = middle + 1;
}
}
return -1;
}