常见排序算法代码整理
2016-11-02 17:03
260 查看
排序算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
plain copy
public void BubbleSort(int[] x)
{
for(int i=0;i<x.length-1;i++) //若数组长度为n,则执行n-1次“冒泡”即可(每次可找出剩余元素的最大值,剩下的一个为最小值)
{
for(int j=0;j<x.length-i-1;j++) //执行第i次“冒泡的时候”在数组尾部已经有i-1个排好序的“最大值”
{
if(x[j]>x[j+1])
swap(x,j,j+1);
}
}
}
public void swap(int[] x,int i,int j)
{
int t=x[i];
x[i]=x[j];
x[j]=t;
}
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定
plain copy
public void Quicksort(int[] x)
{
HelperOfQuicksort(x,0,x.length-1);
}
public void HelperOfQuicksort(int[] x,int low,int high)
{
if(low<high)
{
int mid=getMiddleIndex(x,low,high); //mid左面的部分都小于等于x[mid],mid右面的部分都大于等于x[mid]
HelperOfQuicksort(x,low,mid-1);
HelperOfQuicksort(x,mid+1,high);
}
}
public int getMiddleIndex(int[] x,int low,int high)
{
int tmp=x[low]; //选当前无序部分的第一个元素做标兵
while(low<high)
{
while(low<high&&x[high]>=tmp)
high--;
x[low]=x[high]; //x[high]是本次循环找到的小于tmp的值
while(low<high&&x[low]<=tmp)
low++;
x[high]=x[low]; //x[low]是本次循环找到的大于tmp的值
}
x[low]=tmp; //循环结束后,对应本次函数处理的部分数组(假设从a到b),a到low-1为小于等于tmp的部分,low+1到b为大于等于tmp的部分
return low; //此时low=high
}
时间复杂度:O(n^logn)
空间复杂度:O(n^logn)
稳定性:不稳定
plain copy
public void StraightInsertionSort(int[] x)
{
if(x.length==1)
return ;
for(int i=1;i<x.length;i++) //将数组中的1到x.length-1位置的元素分别插入前面排好序的部分的相应位置(起始的排好序的部分是x[0])
{
int cur=x[i]; //当前待插入元素
int m=i-1; //从当前元素的前一个元素开始比较
for(;m>=0&&x[m]>cur;m--) //前面的元素如果>当前待插入元素则将此元素在数组中后移一个位置
x[m+1]=x[m];
x[m+1]=cur;
//跳出循环时,证明m是小于待插入元素的位置(或者m=-1),将待插入元素插在m+1的位置
}
}
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定
plain copy
public void ShellSort(int[] x)
{
int gap=x.length/2;
while(gap>=1) //最外层循环控制步长
{
for(int i=0;i<gap;i++) //若gap=3,则有三组子序列需要分别进行直接插入排序
{
for(int j=i+gap;j<x.length;j+=gap) //对每一组子序列进行单独进行直接插入排序
{
int cur=x[j];
int index=j-gap;
for(;index>=0&&x[index]>cur;index-=gap)
x[index+gap]=x[index];
x[index+gap]=cur;
}
}
gap/=2;
}
}
时间复杂度:O(n^1.25)到O((1.6n)^1.25)(经验公式)
空间复杂度:O(1)
稳定性:不稳定
plain copy
public void SimpleSelectionSort(int[] x)
{
for(int i=0;i<=x.length-2;i++) //每次找到当前数组剩余元素中的最小值,和剩余部分元素的首元素交换。五个元素交换四次即可。
{ //i指向当前未排序部分(即剩余部分)的首元素
int curMin=Integer.MAX_VALUE;
int index=-1;
for(int j=i;j<x.length;j++) //从i开始直到数组最后找到最小值和它的index
{
if(x[j]<curMin)
{
index=j;
curMin=x[j];
}
}
swap(x,i,index);
}
}
public void swap(int[] x,int i,int j)
{
int t=x[i];
x[i]=x[j];
x[j]=t;
}
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:不稳定([2,2,1],循环首次执行之后1与首元素,即第一个2交换,变成[1,2,2]。第一个二跑到了最后,在原来第二个2之后)
plain copy
public void Heapsort(int[] x)
{
//每次将0到i的元素建堆(只是把数组想作堆结构)后,找到堆顶的元素(即首元素)与i位置的元素交换。即0到i为当前无序部分,i之后都是有序部分
for(int i=x.length-1;i>=1;i--)
{
BuildHeap(x,i);
swap(x,0,i);
}
}
public void BuildHeap(int[] x,int LastIndex)
{
int k=-1;
//从LastIndex到1位置的元素分别找到它们的父结点,如果父结点的值比它的全部子节点的值都大,则不变;否则将父结点的值与子节点中的较大值互换(可能存在当前父结点没有右子节点的情况)
for(int i=LastIndex;i>=1;i--)
{
int curParentNode=(i-1)/2; //(当前结点下标-1)/2即为它的父结点的下标,无论左右结点都适用
//目前的k指向上次循环处理过的父结点,如果k与curParentNode相同,则证明当前的父结点已经处理过了(即它的值肯定大于等于它的子节点的值)
if(curParentNode==k)
continue;
k=curParentNode; //若当前的父结点没有处理过,则更新k
int biggerSonNode=-1; //biggerSonNode指向当前父结点的子节点中值较大的子节点
if(k*2+2<=LastIndex) //k*2+2指向当前父结点的右结点,如果它大于LastIndex,则证明当前父结点没有右子结点
biggerSonNode=x[k*2+1]>=x[k*2+2]?(k*2+1):(k*2+2);
else
biggerSonNode=k*2+1;
if(x[biggerSonNode]>x[k]) //如子节点中较大值大于父结点的值,则交换,保证每一个父结点的值都大于它的所有子节点,最终保证堆顶的元素值最大
swap(x,biggerSonNode,k);
}
}
public void swap(int[] x,int i,int j)
{
int t=x[i];
x[i]=x[j];
x[j]=t;
}
时间复杂度:O(n*logn)
空间复杂度:O(1)
稳定性:不稳定
plain copy
public void MergeSort(int[] x)
{
HelperSort(x,0,x.length-1);
}
public void HelperSort(int[] x,int start,int end) //将当前待处理的部分继续划分为两部分
{
if(start>=end)
return;
int mid=(start+end)/2;
int start1=start; //第一个部分的起始下标
int end1=mid; //第一个部分的终止下标
int start2=mid+1; //第二个部分的起始下标
int end2=end; //第二个部分的终止下标
HelperSort(x,start1,end1);
HelperSort(x,start2,end2);
merge(x,start1,end1,start2,end2); //将划分好的两部分归并。每次归并的两个部分分别都已经是有序的了。
}
public void merge(int[] x,int FirStart,int FirEnd,int SecStart,int SecEnd)
{
int[] temp=new int[SecEnd-FirStart+1]; //临时生成一个新的数组用来存储归并之后的两个部分的合成数组
int tempIndex=0;
int first=FirStart; //指向第一个部分的首个元素
int second=SecStart; //指向第二个部分的首个元素
while(first<=FirEnd&&second<=SecEnd) //直到遍历到某个部分的最后一个元素
{
if(x[first]<=x[second]) //first和second始终指向当前第一(二)个部分剩余元素的最小值
{
temp[tempIndex]=x[first];
tempIndex++;
first++;
}
else
{
temp[tempIndex]=x[second];
tempIndex++;
second++;
}
}
while(first<=FirEnd) //若第一个部分还有剩余没有并入新的临时数组,则直接并入
temp[tempIndex++]=x[first++];
while(second<=SecEnd) //若第二个部分还有剩余没有并入新的临时数组,则直接并入
temp[tempIndex++]=x[second++];
for(int i=0;i<tempIndex;i++) //将并好的有序数组写入原始数组的相应位置(从第一部分的起始到第二部分的结束(因为这两部分在原数组处在相邻位置))
x[FirStart+i]=temp[i];
}
时间复杂度:O(n*logn)
空间复杂度:O(n)
稳定性:稳定
plain copy
public void RadixSort(int[] x)
{
int max=Integer.MIN_VALUE;
for(int i=0;i<x.length;i++) //找到数组中的最大值
{
if(x[i]>max)
max=x[i];
}
int maxbit=0;
while(max>0) //找到最大位数
{
max/=10;
maxbit++;
}
int radix=10; //0~9共10个桶
ArrayList<Queue> list=new ArrayList<>(); //用来存储桶的list。list.get(1)存储着桶1
for(int i=0;i<radix;i++)
{
Queue<Integer> q=new LinkedList<>();
list.add(q);
}
//x中最初存储[5,1,3,7,23,72,7].确认最大位数为十位,故只需整体入桶两次即可(先按照个位后按照十位)
//首先按照个位入桶
//桶1:1
//桶2:72
//桶3:3,23
//桶5:5
//桶7:7,7
//其余的桶为空
//按桶从小到大,同一个桶从左到右的顺序将全部元素放回数组x中(由于使用的是队列,每次头元素入x的同时就会清空桶(队列)中的头元素)
//x为[1,72,3,23,5,7,7],再按照十位入桶
//桶0:1,3,5,7,7
//桶2:23
//桶7:72
//其余的桶为空
//按桶从小到大,同一个桶从左到右的顺序将全部元素放回数组x中
//x为[1,3,5,7,7,23,72],完毕
int dividend=1;
for(int i=0;i<maxbit;i++) //从低位到高位分别处理
{
for(int j1=0;j1<x.length;j1++) //将所有元素按照当前检测位的数字大小入桶
{
int index=(x[j1]/dividend)%radix;
list.get(index).offer(x[j1]);
}
dividend*=10;
int m=0;
for(int j2=0;j2<radix;j2++) //按桶从小到大,同一个桶从左到右的顺序将全部元素放回数组x中
{
while(list.get(j2).size()!=0)
{
x[m]=(int) list.get(j2).poll();
m++;
}
}
}
}
时间复杂度:O(d*n) d为长度(对应代码中maxbit),n为数组长度
空间复杂度:O(n)
稳定性:稳定
1、交换排序
(1)冒泡排序:
[java] viewplain copy
public void BubbleSort(int[] x)
{
for(int i=0;i<x.length-1;i++) //若数组长度为n,则执行n-1次“冒泡”即可(每次可找出剩余元素的最大值,剩下的一个为最小值)
{
for(int j=0;j<x.length-i-1;j++) //执行第i次“冒泡的时候”在数组尾部已经有i-1个排好序的“最大值”
{
if(x[j]>x[j+1])
swap(x,j,j+1);
}
}
}
public void swap(int[] x,int i,int j)
{
int t=x[i];
x[i]=x[j];
x[j]=t;
}
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定
(2)快速排序:
[java] viewplain copy
public void Quicksort(int[] x)
{
HelperOfQuicksort(x,0,x.length-1);
}
public void HelperOfQuicksort(int[] x,int low,int high)
{
if(low<high)
{
int mid=getMiddleIndex(x,low,high); //mid左面的部分都小于等于x[mid],mid右面的部分都大于等于x[mid]
HelperOfQuicksort(x,low,mid-1);
HelperOfQuicksort(x,mid+1,high);
}
}
public int getMiddleIndex(int[] x,int low,int high)
{
int tmp=x[low]; //选当前无序部分的第一个元素做标兵
while(low<high)
{
while(low<high&&x[high]>=tmp)
high--;
x[low]=x[high]; //x[high]是本次循环找到的小于tmp的值
while(low<high&&x[low]<=tmp)
low++;
x[high]=x[low]; //x[low]是本次循环找到的大于tmp的值
}
x[low]=tmp; //循环结束后,对应本次函数处理的部分数组(假设从a到b),a到low-1为小于等于tmp的部分,low+1到b为大于等于tmp的部分
return low; //此时low=high
}
时间复杂度:O(n^logn)
空间复杂度:O(n^logn)
稳定性:不稳定
2、插入排序
(1)直接插入排序:
[java] viewplain copy
public void StraightInsertionSort(int[] x)
{
if(x.length==1)
return ;
for(int i=1;i<x.length;i++) //将数组中的1到x.length-1位置的元素分别插入前面排好序的部分的相应位置(起始的排好序的部分是x[0])
{
int cur=x[i]; //当前待插入元素
int m=i-1; //从当前元素的前一个元素开始比较
for(;m>=0&&x[m]>cur;m--) //前面的元素如果>当前待插入元素则将此元素在数组中后移一个位置
x[m+1]=x[m];
x[m+1]=cur;
//跳出循环时,证明m是小于待插入元素的位置(或者m=-1),将待插入元素插在m+1的位置
}
}
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定
(2)希尔排序:
[java] viewplain copy
public void ShellSort(int[] x)
{
int gap=x.length/2;
while(gap>=1) //最外层循环控制步长
{
for(int i=0;i<gap;i++) //若gap=3,则有三组子序列需要分别进行直接插入排序
{
for(int j=i+gap;j<x.length;j+=gap) //对每一组子序列进行单独进行直接插入排序
{
int cur=x[j];
int index=j-gap;
for(;index>=0&&x[index]>cur;index-=gap)
x[index+gap]=x[index];
x[index+gap]=cur;
}
}
gap/=2;
}
}
时间复杂度:O(n^1.25)到O((1.6n)^1.25)(经验公式)
空间复杂度:O(1)
稳定性:不稳定
3、选择排序
(1)简单选择排序:
[java] viewplain copy
public void SimpleSelectionSort(int[] x)
{
for(int i=0;i<=x.length-2;i++) //每次找到当前数组剩余元素中的最小值,和剩余部分元素的首元素交换。五个元素交换四次即可。
{ //i指向当前未排序部分(即剩余部分)的首元素
int curMin=Integer.MAX_VALUE;
int index=-1;
for(int j=i;j<x.length;j++) //从i开始直到数组最后找到最小值和它的index
{
if(x[j]<curMin)
{
index=j;
curMin=x[j];
}
}
swap(x,i,index);
}
}
public void swap(int[] x,int i,int j)
{
int t=x[i];
x[i]=x[j];
x[j]=t;
}
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:不稳定([2,2,1],循环首次执行之后1与首元素,即第一个2交换,变成[1,2,2]。第一个二跑到了最后,在原来第二个2之后)
(2)堆排序
[java] viewplain copy
public void Heapsort(int[] x)
{
//每次将0到i的元素建堆(只是把数组想作堆结构)后,找到堆顶的元素(即首元素)与i位置的元素交换。即0到i为当前无序部分,i之后都是有序部分
for(int i=x.length-1;i>=1;i--)
{
BuildHeap(x,i);
swap(x,0,i);
}
}
public void BuildHeap(int[] x,int LastIndex)
{
int k=-1;
//从LastIndex到1位置的元素分别找到它们的父结点,如果父结点的值比它的全部子节点的值都大,则不变;否则将父结点的值与子节点中的较大值互换(可能存在当前父结点没有右子节点的情况)
for(int i=LastIndex;i>=1;i--)
{
int curParentNode=(i-1)/2; //(当前结点下标-1)/2即为它的父结点的下标,无论左右结点都适用
//目前的k指向上次循环处理过的父结点,如果k与curParentNode相同,则证明当前的父结点已经处理过了(即它的值肯定大于等于它的子节点的值)
if(curParentNode==k)
continue;
k=curParentNode; //若当前的父结点没有处理过,则更新k
int biggerSonNode=-1; //biggerSonNode指向当前父结点的子节点中值较大的子节点
if(k*2+2<=LastIndex) //k*2+2指向当前父结点的右结点,如果它大于LastIndex,则证明当前父结点没有右子结点
biggerSonNode=x[k*2+1]>=x[k*2+2]?(k*2+1):(k*2+2);
else
biggerSonNode=k*2+1;
if(x[biggerSonNode]>x[k]) //如子节点中较大值大于父结点的值,则交换,保证每一个父结点的值都大于它的所有子节点,最终保证堆顶的元素值最大
swap(x,biggerSonNode,k);
}
}
public void swap(int[] x,int i,int j)
{
int t=x[i];
x[i]=x[j];
x[j]=t;
}
时间复杂度:O(n*logn)
空间复杂度:O(1)
稳定性:不稳定
4、归并排序
[java] viewplain copy
public void MergeSort(int[] x)
{
HelperSort(x,0,x.length-1);
}
public void HelperSort(int[] x,int start,int end) //将当前待处理的部分继续划分为两部分
{
if(start>=end)
return;
int mid=(start+end)/2;
int start1=start; //第一个部分的起始下标
int end1=mid; //第一个部分的终止下标
int start2=mid+1; //第二个部分的起始下标
int end2=end; //第二个部分的终止下标
HelperSort(x,start1,end1);
HelperSort(x,start2,end2);
merge(x,start1,end1,start2,end2); //将划分好的两部分归并。每次归并的两个部分分别都已经是有序的了。
}
public void merge(int[] x,int FirStart,int FirEnd,int SecStart,int SecEnd)
{
int[] temp=new int[SecEnd-FirStart+1]; //临时生成一个新的数组用来存储归并之后的两个部分的合成数组
int tempIndex=0;
int first=FirStart; //指向第一个部分的首个元素
int second=SecStart; //指向第二个部分的首个元素
while(first<=FirEnd&&second<=SecEnd) //直到遍历到某个部分的最后一个元素
{
if(x[first]<=x[second]) //first和second始终指向当前第一(二)个部分剩余元素的最小值
{
temp[tempIndex]=x[first];
tempIndex++;
first++;
}
else
{
temp[tempIndex]=x[second];
tempIndex++;
second++;
}
}
while(first<=FirEnd) //若第一个部分还有剩余没有并入新的临时数组,则直接并入
temp[tempIndex++]=x[first++];
while(second<=SecEnd) //若第二个部分还有剩余没有并入新的临时数组,则直接并入
temp[tempIndex++]=x[second++];
for(int i=0;i<tempIndex;i++) //将并好的有序数组写入原始数组的相应位置(从第一部分的起始到第二部分的结束(因为这两部分在原数组处在相邻位置))
x[FirStart+i]=temp[i];
}
时间复杂度:O(n*logn)
空间复杂度:O(n)
稳定性:稳定
5、基数排序
[java] viewplain copy
public void RadixSort(int[] x)
{
int max=Integer.MIN_VALUE;
for(int i=0;i<x.length;i++) //找到数组中的最大值
{
if(x[i]>max)
max=x[i];
}
int maxbit=0;
while(max>0) //找到最大位数
{
max/=10;
maxbit++;
}
int radix=10; //0~9共10个桶
ArrayList<Queue> list=new ArrayList<>(); //用来存储桶的list。list.get(1)存储着桶1
for(int i=0;i<radix;i++)
{
Queue<Integer> q=new LinkedList<>();
list.add(q);
}
//x中最初存储[5,1,3,7,23,72,7].确认最大位数为十位,故只需整体入桶两次即可(先按照个位后按照十位)
//首先按照个位入桶
//桶1:1
//桶2:72
//桶3:3,23
//桶5:5
//桶7:7,7
//其余的桶为空
//按桶从小到大,同一个桶从左到右的顺序将全部元素放回数组x中(由于使用的是队列,每次头元素入x的同时就会清空桶(队列)中的头元素)
//x为[1,72,3,23,5,7,7],再按照十位入桶
//桶0:1,3,5,7,7
//桶2:23
//桶7:72
//其余的桶为空
//按桶从小到大,同一个桶从左到右的顺序将全部元素放回数组x中
//x为[1,3,5,7,7,23,72],完毕
int dividend=1;
for(int i=0;i<maxbit;i++) //从低位到高位分别处理
{
for(int j1=0;j1<x.length;j1++) //将所有元素按照当前检测位的数字大小入桶
{
int index=(x[j1]/dividend)%radix;
list.get(index).offer(x[j1]);
}
dividend*=10;
int m=0;
for(int j2=0;j2<radix;j2++) //按桶从小到大,同一个桶从左到右的顺序将全部元素放回数组x中
{
while(list.get(j2).size()!=0)
{
x[m]=(int) list.get(j2).poll();
m++;
}
}
}
}
时间复杂度:O(d*n) d为长度(对应代码中maxbit),n为数组长度
空间复杂度:O(n)
稳定性:稳定
相关文章推荐
- 常见排序算法代码
- 整理收藏 ASP中常见脚本精华代码
- 网页常见错误代码分析整理
- Java实现几种常见排序算法代码
- 【整理】常见的数组排序方法以及代码实现
- 整理收藏 ASP中常见脚本精华代码
- 几种常见的排序算法整理
- 常见排序算法整理
- java的各种排序算法代码整理
- 几种常见排序算法的Java代码实现
- 排序算法:几种常见的排序算法的C语言代码
- 整理java代码常见应用
- 搜集整理的HTTP常见状态代码
- as3种常见的弹性效果公式以及波形运动等as3动画效果公式代码整理
- 精心整理的常见jquery代码片段(27个)
- 常见程序OEP处代码整理
- android常见代码整理
- iOS开发常见错误代码对照表整理
- 排序算法整理-带C代码
- Java实现几种常见排序算法代码