八种排序

Java 的八种排序方法
1 冒泡排序
2 直接插入排序
3 选择排序
4 希尔排序
5 快速排序
6 归并排序
7 堆排序
8 基数排序
1 冒泡排序
基本思想:对比相邻的元素值,如果满足条件就交换元素值。把较小的元素放在前面,把较
大的元素放在后面
时间复杂度:最好:O(n)平均:O(n^2) 最坏: O(n^2)
稳定性: 稳定
代码
package kill;
import java.util.Arrays;
public class TestDemo1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//int[]array ={3,8,9,6,4,2,7};
int[]array=new int[10000];//声明并初始化一维数组
for(int i =0;i <array.length;i++){
array[i] = (int)(Math.random()*10+1);
}
bubbleSort(array);
System.out.println(Arrays.toString(array));
}
public static void bubbleSort(int[] array){
System.out.println(System.currentTimeMillis()); //开始语句
int tmp = 0;//定义一个临时变量
for(int i =0;i <array.length-1;i++){//趟数
for(int j =0;j <array.length-1-i;j++){//次数 比较两个数,较大的
数往后
if(array[j]> array[j+1]){
tmp =array[j];//把第一个值保留在临时变量中
array[j] = array[j+1];//把第二个元素值保留在第一个元素单元中
array[j+1] = tmp;//把临时变量的值赋给第二个元素单元
}
}
}System.out.println(System.currentTimeMillis());//结束语句 用来计算时长
}
}
结果:1526901586976
1526901587210
111……222……333……444……10 10 10
冒泡方法还有一种优化
我们试想一下要是整个序列本身就是有序的就没有必要在进行冒泡排序了,可是系统还是会
进行排序。那么此时就要优化冒泡排序了。我们加入一个判断语句,如果有序就 break 出去。
package kill;
import java.util.Arrays;
public class TestDemo2 {
public static void bubbleSort2(int[] array){
System.out.println(System.currentTimeMillis()); //开始语句
int tmp = 0;
for(int i =0;i <array.length-1;i++){//趟数
boolean swap = false;
//当前面的数比后面的数大时执行for
for(int j =0;j <array.length-1-i;j++){
if(array[j]> array[j+1]){
tmp =array[j];
array[j] = array[j+1];
array[j+1] = tmp;
swap = true;
}
}
if(!swap){//swap == false
break;
}
}
System.out.println(System.currentTimeMillis());//结束语句 用来计算时
长
}
public static void main(String[] args) {
int[]array=new int[10000];
for(int i =0;i <array.length;i++){
array[i] = (int)(Math.random()*10+1);
}
bubbleSort2(array);
System.out.println(Arrays.toString(array));
}
}
结果
1526903765884
1526903766101
2 直接插入排序
插入排序算法的思路:
(1)将整个数组划分成两部分:有序区和无序区。初始情况下,有序区包括数组的第一元素,
无序区包括剩余的其他元素。
(2)遍历无序区,每次向有序区增加一个元素,在增加元素后要保证有序区的有序性。就是把
无序区的第一个数插到有序区中,并保证有序区依旧有序
时间复杂度:最好:O(n)平均:O(n^2) 最坏: O(n^2)
稳定性: 稳定
package kill;
import java.util.Arrays;
public class TestDemo10{
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] array={23,1,32,87,57,33,19,7,99,13};
insrtSort(array);
System.out.println(Arrays.toString(array));
}
public static void insrtSort(int []array){
//定义有序数组的下标
int j;
//从第二个元素开始遍历数组
for(int i=1;i<array.length;i++){
//保存新增加的元素
inttmp =array[i];
for(j= i-1;j >= 0;j--){
if(array[j]>tmp){
array[j+1]=array[j];//覆盖后一位的值
}else{//每次排序后已经有序,找到第一个比temp小的;
break;//有序就跳出
}
}
array[j+1]=tmp;
}
}
}
结果: [1, 7, 13, 19, 23, 32, 33, 57, 87, 99]
选择排序
/*
选择排序算法的思路:
(1)将序列分成有序区和无序区两部分,初始时有序区为空,无序区包括全部元素。
(2)每次从无序区中选择最小的元素将其与无序区第一个元素进行交换。
(3)重复步骤(2),直到无序区没有元素。
*/
时间复杂度:最好:O(n^2)平均:O(n^2) 最坏: O(n^2)
稳定性: 不稳定
package kill;
import java.util.Arrays;
public class TestDemo8 {
public static void main(String[] args) {
int [] array={23,1,32,87,57,33,19,7,13};
selectSort(array);
System.out.println(Arrays.toString(array));
// TODO Auto-generated method stub
}
public static void selectSort(int []array){
int minIndex=0;//保存最小元素的下标
int tmp;
//遍历有序区的元素
for(int i=0;i<array.length;i++){
minIndex=i;
//查找最小元素在数组的下标
for(int j =i + 1;j < array.length;j++){
//
if(array[minIndex]>array[j]){
minIndex=j;//保存最小数的下标最小数的
}
}
//把最小的数放在第 i 位
tmp=array[i];
array[i]=array[minIndex];
array[minIndex]=tmp;
}
}
}
希尔排序
在看希尔排序之前我们回顾一下直接插入排序的思想
直接插入排序中 刚开始给的序列越有序越快
回顾插入排序:越有序越快 假如有10000 个数进行排序那么直接插入排序的时间复杂度为
10000*10000
如果采用分组思想,让组内进行直接插入排序那么时间复杂度就为 100*100*100
我们希尔排序就是利用分组思想进行排序的 让每组数据变得有序 越有序越快
时间复杂度:最好:O(n)平均:O(n^1.3) 最坏: O(n^2)
稳定性: 不稳定
写程序时要件进行分组,产生组的概念(记得产生的组必须互为素数,且最后一个增量必须是 1),
然后再进行每一组的直接插入排序 .
packagesort;
importjava.util.Arrays;
publicclassTestShellSortDemo{
publicstaticvoidshellSort(int[]array){
//产生组的概念
int[]d={5,3,1};
for(inti=0;i<d.length;i++){
shell(array,d[i]);
}
该文档是极速PDF编辑器生成,
如果想去掉该提示,请访问并下载:
http://www.jisupdfeditor.com/
}
//每一组进行直接插入排序
publicstaticvoidshell(int[]array,intgap){
inttmp=0;
inti=0;
intj=0;
for(i=gap;i<array.length;i++){//这里一定注意是i++,如果
是i+=gap就会让剩下的几组没有排
tmp=array[i];
for(j=i-gap;j>=0;j-=gap){
if(array[j]>tmp){
array[j+gap]=array[j];
}else{
break;
}
}
array[j+gap]=tmp;
}
}
publicstaticvoidmain(String[]args){
//TODOAuto-generatedmethodstub
int[]array={22,77,99,11,55,33,66,88,44,55,23,12,54,16,87};
shellSort(array);
System.out.println(Arrays.toString(array));
}
}
快速排序
有三种方法:固定取基准法
随机取基准法
三分取基准法
时间复杂度:最好:O(nlog2(n)) 平均:O(nlog2(n)) 最坏: O(n^2)
稳定性: 不稳定
固定取基准法
快速排序(递归和非递归)
一趟快排得到的结果
找一次基准的时间复杂度为O(n^2)
对于一个未排序的数组,经过一趟快排,此时par已经确定
如果par之前或者之后还有至少两个数那就还得找基准进行一趟快排,找出新
的par
递归方法:
packagesort;
importjava.util.Arrays;
publicclassTestQuickSort{
//返回基准的下标
publicstaticintpartion(int[]array,intlow,inthign){
inttmp=array[low];
while(low<hign){
while(low<hign&&array[hign]>=tmp){
--hign;
}
if(low>=hign){
break;
}else{
array[low]=array[hign];
}
while(low<hign&&array[low]<=tmp){
++low;
}
if(low>=hign){
break;
}else{
array[hign]=array[low];
}
}
array[low]=tmp;
returnlow;
}
publicstaticvoidQuick(int[]array,intlow,inthign){
intpar=partion(array,low,hign);
if(par>low+1){//左边还需要快排
Quick(array,low,par-1);
}
if(par<hign-1){//右边还需要快排
Quick(array,par+1,hign);
}
}
publicstaticvoidQuickSort(int[]array){
Quick(array,0,array.length-1);
}
public static void main(String[] args) { // TODO Auto-generated method
stub
int[]array={20,3,5,0,32,22,11,8,7,9};
QuickSort(array);
System.out.println(Arrays.toString(array));
}
}
非递归:
根据上个例子:
packagesort;
import java.util.Arrays;
public class TestQuickSort {
public staticintpartion(int[]array,intlow,inthign){
inttmp=array[low];
while(low<hign){
while(low<hign&&array[hign]>=tmp){
--hign;
}
if(low>=hign){
break;
}else{
array[low]=array[hign];
}
while(low<hign&&array[low]<=tmp){
++low;
}
if(low>=hign){
break;
}else{
array[hign]=array[low];
}
}
array[low]=tmp;
return low;
}
//快速排序的非递归用栈来做
public staticvoid QuickSort(int[]array){
int[]stack=new int[array.length];
inttop=0;
intlow=0;
inthign=array.length-1;
intpar=partion(array,low,hign);
//入栈
if(par>low+1){
stack[top++]=low;
stack[top++]=par-1;
}
if(par<hign-1){
stack[top++]=par+1;
stack[top++]=hign;
}
//出栈
while(top>0){
hign=stack[--top];
low=stack[--top];
par=partion(array,low,hign);
if(par>low+1){
stack[top++]=low;
stack[top++]=par-1;
}
if(par<hign-1){
stack[top++]=par+1;
stack[top++]=hign;
}
}
}
public staticvoid main(String[] args) {
// TODOAuto-generated methodstub
int[]array={20,3,5,0,32,22,11,8,7,9};
QuickSort(array);
System.out.println(Arrays.toString(array));
}
}
上面采用的是以 0号位置的数来做基准在待排序序列是不分有序时,固定选取基准是快排效
率低下,要换届这种情况,就引入了随机选取基准
随机取基准
思想:取待排序列中任意一个元素作为基准
引入的原因:在待排序序列是不分有序时,固定选取基准是快排效率低下,要
换届这种情况,就引入了随机选取基准
package sort;
//随机取基准
import java.util.Arrays;
import java.util.Random;
public class TestQuickSort2 {
//找到序列的基准并返回基准的下标
public static int partion(int []array,int low,int hign){
int tmp=array[low];
while(low<hign){
while(low<hign&&array[hign]>=tmp){
--hign;
}
if(low>=hign){
break;
}else{
array[low]=array[hign];
}
while(low<hign&&array[low]<=tmp){
++low;
}
if(low>=hign){
break;
}else{
array[hign]=array[low];
}
}
array[low]=tmp;
return low;
}
//利用 swap 方法将随机的基准对应的数与首位置对应的数互换
public static void swap(int[]array,intstart,int end){
int tmp=array[start];
array[start]=array[end];
array[end]=tmp;
}
//采用递归的方法再对基准左右边进行快排
public static void Quick(int[]array,int start,int end){
//swap(array,start,(int)Math.random()%(end-start+1)+start); //老师教
改过来
Random rand=new Random();
int randNumber=rand.nextInt(end-start+1)+start;//产生基准的随机数
swap(array,start,randNumber);
int par=partion(array,start,end);
if(par>start+1){//左边还需要快排
Quick(array,start,par-1);
}
if(par<end-1){//右边还需要快排
Quick(array,par+1,end);
}
}
public static void QuickSort2(int[]array){
Quick(array,0,array.length-1);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[]array={1,4,6,7,6,6,7,6,8,6};
QuickSort2(array);
System.out.println(Arrays.toString(array));
}
}
三分取基准法
//三分取基准法
package sort;
import java.util.Arrays;
public class TestQuickSort4 {
public static int partion(int []array,int low,int hign){
int tmp=array[low];
while(low<hign){
while(low<hign&&array[hign]>=tmp){
--hign;
}
if(low>=hign){
break;
}else{
array[low]=array[hign];
}
while(low<hign&&array[low]<=tmp){
++low;
}
if(low>=hign){
break;
}else{
array[hign]=array[low];
}
}
array[low]=tmp;
return low;
}
//三分取基准
取low,high,和mid,将他们对应的数中处于中间大小的数作为基准进行排序,这样可以
有效避免有序数列所带来的较大的时间复杂度
public static void new_Sort(int[]array,int start,int end){
//mid为start和end的中间值,代表的是下标
int mid=start+(end-start)>>1;
if(array[mid]>array[end]){//mid和end对应的大的数给array【end】
swap(array,end,mid);
}
if(array[start]>array[end]){//start和end对应的大的数给array【end】
swap(array,start,end);
}
if(array[mid]>array[start]){//mid和start对应大的数给array【start】
swap(array,mid,start);
}
//最终目标 array[mid]<array[start]<array[end]
}
public static void swap(int[]array,int start,int end){
int tmp=array[start];
array[start]=array[end];
array[end]=tmp;
}
public static void Quick(int[]array,int start,int end){
new_Sort(array,start,end);
int par=partion(array,start,end);
if(par>start+1){//左边还需要快排
Quick(array,start,par-1);
}
if(par<end-1){//右边还需要快排
Quick(array,par+1,end);
}
}
public static void QuickSort4(int[]array){
Quick(array,0,array.length-1);
}
public static void main(String[] args) {
// TODOAuto-generated method stub
int[]array={1,4,6,7,6,6,7,6,8,6};
QuickSort4(array);
System.out.println(Arrays.toString(array));
}
}
结果:[1, 4, 6, 6, 6, 6, 6, 7, 7, 8]
以上是快速排序的三种方法
后两者应该算是快速排序的两种简单意义的优化
真正的优化有两种方法:
1. 当数据少的情况下直接插入排序比快速排序更快一点
2 聚集相同元素法 这种方法可以减少递归的次数
法 相同元素聚集法  快速排序的一种优化
在一次分割结束后可以把与 par 相等的元素聚集在一起,继续下次分割时,不用再对与par 相
等元素分割
举例:
待排序序列 1 4 6 76 6 7 6 8 6
划分后,对与 par 元素相等处理的结果:1 4 6 6 6 6 6 7 8 7
下次的两个子序列为:1 4 和 7 87
经过对比,我们可以看出,在一次划分后,把与 par 相等的元素聚在一起,能减少迭代次数,
效率会提高不少
具体过程:在处理过程中,会有两个步骤
第一步,在划分过程中,把与 par 相等元素放入数组的两端
第二步,划分结束后,把与 par 相等的元素移到 par 周围
//聚集相同元素法
packagesort;
importjava.util.Arrays;
/*
*快速排序的一种优化
*
*/
publicclassTestQuickSort3{
//一趟快排
publicstaticintpartion(int[]array,intlow,inthign){
inttmp=array[low];
while(low<hign){
while(low<hign&&array[hign]>=tmp){
--hign;
}
if(low>=hign){
break;
}else{
array[low]=array[hign];
}
while(low<hign&&array[low]<=tmp){
++low;
}
if(low>=hign){
break;
}else{
array[hign]=array[low];
}
}
array[low]=tmp;
returnlow;
}
//定义聚集方法
publicstaticint[]focusNum(int[]array,intstart,intend,int
par,intleft,intright){
intparLeft=par-1;
intparRight=par+1;
inttmp=0;
//左边寻找
for(inti=par-1;i>=start;i--){
if(array[i]==array[par]){
if(i!=parLeft){
tmp=array[parLeft];
array[parLeft]=array[i];
array[i]=tmp;
parLeft--;
}else{
parLeft--;
}
}
}
//右边寻找
for(intj=par+1;j<=end;j++){
if(array[j]==array[par]){
if(j!=parRight){
tmp=array[parRight];
array[parRight]=array[j];
array[j]=tmp;
parRight++;
}else{
parRight++;
}
}
}
right=parRight;//聚集的相同元素最右边的一个非相同元素
left=parLeft;//聚集的相同元素最左边的一个非相同元素
int[]brray=newint[2];
brray[0]=left;
brray[1]=right;
returnbrray;
}
publicstaticvoidQuick(int[]array,intstart,intend){
intpar=partion(array,start,end);
intleft=par-1;
intright=par+1;
int[]brray=focusNum(array,start,end,par,left,right);
left=brray[0];
right=brray[1];
if(par>start+1){//左边还需要快排
Quick(array,start,left);
}
if(par<end-1){//右边还需要快排
Quick(array,right,end);
}
}
publicstaticvoidQuickSort(int[]array){
Quick(array,0,array.length-1);
}
publicstaticvoidmain(String[]args){
//TODOAuto-generatedmethodstub
int[]array={1,4,6,6,6,6,6,7,7,8};
QuickSort(array);
System.out.println(Arrays.toString(array));
}
}
堆排序
时间复杂度:最好:O(nlog2(n)) 平均:O(nlog2(n)) 最坏: O(nlog2(n))
稳定性: 不稳定
堆排序: 小根堆 大根堆
我们从小到大排序采用的是大根堆
需要了解的公式:已知子推父:(n-1)/2
已知父推子:2n+1 2n+2
package heapsort;
import java.util.Arrays;
public class TestHeapSortDemo {
public static void adjustNum(int[]array,int start,int end) {
int tmp = array[start];
for(int i =2*start+1;i<= end;i = 2*i+1) {
if(i < end && array[i] < array[i+1]) {
i++;
}
if(array[i]> tmp) {
array[start] = array[i];
start= i;
}
if(array[i]< tmp) {
break;
}
}
array[start] = tmp;
}
public static void heapSort(int[]array) {
for(int i =(array.length-1-1)/2;i >= 0;i--) {
adjustNum(array,i,array.length-1);
}
int tmp = 0;
for(int j =0;j <= array.length-1;j++) {
tmp =array[0];
array[0] = array[array.length-1-j];
array[array.length-1-j]= tmp;
adjustNum(array,0,array.length-1-1-j);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[]array={12,34,45,21,4,6,33,53,15,5};
heapSort(array);
System.out.println(Arrays.toString(array));
}
}
归并排序
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一
个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个
子序列有序,再使子序列段间有序。
时间复杂度:最好:O(nlog2(n)) 平均:O(nlog2(n)) 最坏: O(nlog2(n))
稳定性: 稳定
packagemergesort;
importjava.util.Arrays;
publicclassTestMergeSort{
//归并排序
publicstaticvoidmergeSort(int[]array){
//遍历序列 产生每归并段1个数 2个数 4个数
for(inti=1;i<array.length;i=i*2){
merge(array,i);
}
}
public static void merge(int[]array,int gap) {//gap 为每个归并段的
个数
intstart1=0;
intend1=start1+gap-1;
intstart2=end1+1;
intend2=start2+gap-1<array.length-1?
start2+gap-1:array.length-1;
//进行排序
int[]brray=newint[array.length];
inti=0;
while(start2<array.length){//肯定有两个归并段
while(start1 <= end1 && start2 <= end2){//两个归并段是否都
有数据
if(array[start1]<array[start2]){
brray[i++]=array[start1++];
}else{
brray[i++]=array[start2++];
}
}
while(start1<=end1){//没有第二个归并段了
brray[i++]=array[start1++];
}
while(start2<=end2){//第一个归并段都放到brray了
brray[i++]=array[start2++];
}
//重新归位 赶下一个归并段
start1=end2+1;
end1=start1+gap-1;
start2=end1+1;
end2=start2+gap-1<array.length-1?
start2+gap-1:array.length-1;
}
//剩下的start1和end1之间的的数据都放到brray中
while(start1<array.length){
brray[i++]=array[start1++];
}
//将已经有序的中间数组(tmp)的数据放回原来的数组中
for(intj=0;j<brray.length;j++){
array[j]=brray[j];
}
}
publicstaticvoidmain(String[]args){
int[]array={11,17,23,12,3,35,7};
mergeSort(array);
System.out.println(Arrays.toString(array));
}
}
基数算法
时间复杂度:最好:O(d(rd+n)) 平均:O(d(r+n)) 最坏: O(d(r+n))
稳定性: 稳定

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值