快速排序和简单排序
本人因为最近工作用到了一些排序算法,就把几个简单的排序算法,想冒泡排序,选择排序,插入排序,奇偶排序和快速排序等整理了出来,代码用C#代码实现,并且通过了测试。希望能给大家提供参考。
1.冒泡排序
冒泡排序,是指计算机的一种排序算法,它的时间复杂度是O(n^2),虽然不及堆排序和快速排序时间复杂度为O(nlogn,底数为2),但是有两个优点:1:编程复杂度低,很容易实现;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]选择关键字最小的元素R[k],将它与无序区的R[1] 进行交换,使R[1...1]和R[2...n]变为个增加为1新有序区,和无序区的元素个数减去1的新无序区。
第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(1≤i≤n-1)。该趟排序从当前无序区中选出关键字最小的记录 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] = R[high];
4. 从low 开始向后搜索,即由前开始向后搜索,low=low+1,找到第一个大于key的值R[low],并执行R[high] = 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 key= R[low];
while (high > low)
{
while (low < high && key<= R[high])
{
high--;
}
if (high > low)
{
R[low] = R[high];
R[high] = key;
}
while (low < high && key>= R[low])
{
low++;
}
if (high > low)
{
R[high] = R[low];
R[low] = key;
}
}
if (low == high)
{
if (Low < low - 1)
{
stack.Push(low - 1);
stack.Push(Low);
}
if (High > low + 1)
{
stack.Push(High);
stack.Push(low + 1);
}
}
}
测试代码:
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(arry.Length - 1);
s.Push(0);
int low = 0;
int high = 0 ;
while (s.Count > 0)
{
low = s.Pop();
high = s.Pop();
QuickSort(arry, 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;
}