最新整理c#排序方法.docx
《最新整理c#排序方法.docx》由会员分享,可在线阅读,更多相关《最新整理c#排序方法.docx(10页珍藏版)》请在冰豆网上搜索。
最新整理c#排序方法
2011.4.11
(1)冒泡排序
依次比较相邻的两个数,将小数放在前面,大数放在后面。
即在第一趟:
首先比较第1个和第2个数,将小数放前,大数放后。
然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。
至此第一趟结束,将最大的数放到了最后。
在第二趟:
仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。
如此下去,重复以上过程,直至最终完成排序。
冒泡排序是一种稳定排序算法。
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
namespaceConsoleApplication3
{
classProgram
{
staticvoidMain(string[]args)
{
int[]a={3,4,7,10,5,9};
int[]b=BubbleSort(a);
for(inti=0;i {
Console.Write(b[i].ToString()+"");
}
Console.ReadLine();
}
publicstaticint[]BubbleSort(int[]list)
{
inti,temp;
for(intj=0;j {
for(i=list.Length-1;i>j;i--)
{
if(list[j] {
temp=list[j];
list[j]=list[i];
list[i]=temp;
}
}
}
returnlist;
}
}
}
(2)选择排序
定位比较交换法:
设有10个数分别存在数组元素a[0]~a[9]中。
定位比较交换法是由大到小依次定位a[0]~a[9]中恰当的值(和武林大会中的比武差不多),a[9]中放的自然是最小的数。
如定位a[0],先假定a[0]中当前值是最大数,a[0]与后面的元素一一比较,如果a[4]更大,则将a[0]、a[4]交换,a[0]已更新再与后面的a[5]~a[9]比较,如果a[8]还要大,则将a[0]、a[8]交换,a[0]又是新数,再与a[9]比较。
一轮比完以后,a[0]就是最大的数了,本次比武的武状元诞生了,接下来从a[1]开始,因为状元要休息了,再来一轮a[1]就是次大的数,也就是榜眼,然后从a[2]开始,比出探花,真成比武大会了,当比到a[8]以后,排序就完成了。
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。
那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。
比较拗口,举个例子,序列58529,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。
publicvoidSort(int[]list)
{
//外层循环数组
for(inti=0;i{
//初始第一个值为最小值
min=i;
//内层循环,从外层循环的数组元素的后一个元素开始
for(intj=i+1;j{
//找到最小的那个元素
if(list[j]min=j;
}
//把最小的元素放到数组的最前面,然后后面的元素继续循环
intt=list[min];
list[min]=list[i];
list[i]=t;
}
}
(3)插入排序
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。
当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。
比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。
如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。
所以插入排序是稳定的。
publicstaticvoidinsertSort(int[]temp)
{
intlength=temp.length;
for(inti=1;i{
inttempNo=temp[i];
for(intj=0;j
{
if(tempNo{
for(intk=i;k>j;k--)//将其遍历数和比较数之间的数依次向后移动一位
temp[k]=temp[k-1];
temp[j]=tempNo;
}}}}
(4)快速排序
快速排序有两个方向,左边的i下标一直往右走,当a[i]<=a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。
而右边的j下标一直往左走,当a[j]>a[center_index]。
如果i和j都走不动了,i<=j,交换a[i]和a[j],重复上面的过程,直到i>j。
交换a[j]和a[center_index],完成一趟快速排序。
在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为53343891011,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序不是一个稳定的排序算法,不稳定发生在中枢元素和a[j]交换的时刻。
namespacetemp
{
publicclassQuickSort
{
///
///排序
///
///待排序数组
///数组第一个元素索引Index
///数组最后一个元素索引Index
privatestaticvoidSort(int[]numbers,intleft,intright)
{
//左边索引小于右边,则还未排序完成
if(left {
//取中间的元素作为比较基准,小于他的往左边移,大于他的往右边移
intmiddle=numbers[(left+right)/2];
inti=left-1;
intj=right+1;
while(true)
{
while(numbers[++i] while(numbers[--j]>middle);
if(i>=j)
break;
Swap(numbers,i,j);
}
Sort(numbers,left,i-1);
Sort(numbers,j+1,right);
}
}
///
///交换元素值
///
///数组
///当前左边索引
///当前右边索引
privatestaticvoidSwap(int[]numbers,inti,intj)
{
intnumber=numbers[i];
numbers[i]=numbers[j];
numbers[j]=number;
}
publicstaticvoidMain()
{
int[]max={6,5,2,9,7,4,0};
Sort(max,0,max.Length-1);
StringBuildertemp=newStringBuilder();
for(inti=0;i {
temp.Append(max[i].ToString()+",");
}
Console.WriteLine(temp.ToString().Substring(0,temp.Length-1));
Console.ReadLine();
}}}
(5)归并排序
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。
所以,归并排序也是稳定的排序算法。
//归并排序
publicvoidMergeSort(intlow,inthigh,int[]a)
{
intmid,i,j,k;
int[]b=newint[high+1];
if(low>=high)
{
return;
}
mid=(low+high)/2;
MergeSort(low,mid,a);
MergeSort(mid+1,high,a);
i=low;
j=mid+1;
k=low;
while((i<=mid)&&(j<=high))
{
if(a[i]<=a[j])
{
b[k]=a[i];
i++;
}
else
{
b[k]=a[j];
j++;
}
k++;
}
while(j<=high)//如果第二个中仍然有某些元素追加到新列表的子列表
{
b[k]=a[j];
j++;
k++;
}
while(i<=mid)//如果在第一个子列表中仍然有一些元素将它们追加到新类别中
{
b[k]=a[i];
i++;
k++;
}
for(intii=low;ii<=high;ii++)
{
a[ii]=b[ii];
}
}
publicvoiddisplay(int[]a)
{
intn=a.Length;
Console.WriteLine("排序后的数据:
");
for(inti=0;i{
Console.WriteLine(a[i]);
}
}
}
(6)基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。
有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
基数排序基于分别排序,分别收集,所以基数排序是稳定的排序算法。
(7)希尔排序(shell)
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。
所以,希尔排序的时间复杂度会比o(n^2)好一些。
由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
usingSystem;
publicclassShellSorter
{
publicvoidSort(int[]list)
{
intinc;
for(inc=1;inc<=list.Length/9;inc=3*inc+1);
for(;inc>0;inc/=3)
{
for(inti=inc+1;i<=list.Length;i+=inc)
{
intt=list[i-1];
intj=i;
while((j>inc)&&(list[j-inc-1]>t))
{
list[j-1]=list[j-inc-1];
j-=inc;
}
list[j-1]=t;
}
}
}
}
publicclassMainClass
{
publicstaticvoidMain()
{
int[]iArrary=newint[]{1,5,3,6,10,55,9,2,87,12,34,75,33,47};
ShellSortersh=newShellSorter();
sh.Sort(iArrary);
for(intm=0;m<=13;m++)
Console.WriteLine("{0}",iArrary[m]);
Console.ReadKey();
}
}
(8)堆排序
我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。
在一个长为n的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。
但当为n/2-1,n/2-2,...1这些个父节点选择元素时,就会破坏稳定性。
有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。
所以,堆排序不是稳定的排序算法。
#region堆
///
///建成大堆
///
///
///
///
voidHeapAdjust(int[]arr,inti,intlength)
{
intchild=2*i+1;//左节点
inttemp=arr[i];//中间变量保存当前根节点
while(child {
//如果有右节点,判断是否大于左节点
if(child child++;
//双亲节点大于子节点
if(temp>=arr[child])
break;//不需调整,结束调整
arr[i]=arr[child];//双亲结点值设置为大的子节点值
i=child;
child=2*i+1;
}
arr[i]=temp;
}
publicvoidHeap(int[]arr)
{
//第一次创建大堆
for(inti=arr.Length/2-1;i>=0;i--)
{
HeapAdjust(arr,i,arr.Length);
}
//元素位置调换
for(inti=arr.Length-1;i>0;i--)
{
//堆顶与当前堆的最后一个堆元素交换位置
inttmp=arr[0];
arr[0]=arr[i];
arr[i]=tmp;
//将剩下的无序堆部分重新建堆处理
HeapAdjust(arr,0,i);
foreach(intvinarr)
{
Console.Write(v.ToString()+"");
}
Console.WriteLine("");
}
}
#endregion
综上,得出结论:
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
还有一些排序算法我没有进行