个人整理——排序算法及其代码实现

一、常见算法比较

 

 

 

图片名词解释:

  • n: 数据规模
  • k: “桶”的个数
  • In-place: 占用常数内存,不占用额外内存
  • Out-place: 占用额外内存

 

 

二、概括及其实现

(1)冒泡排序


冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。元素相等不交换,如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。 每趟排序必确定一个数值的最终位置。

代码实现

package bubbleSort;

public class Bubblesort {
	
	
	    public static void main(String[] args) {
	        //冒泡排序算法
	        int[] numbers=new int[]{1,5,8,2,3,9,4};
	        int i,j;
	        for(i=0;i<numbers.length-1;i++)
	        {
	            for(j=0;j<numbers.length-1-i;j++)
	            {
	                if(numbers[j]>numbers[j+1])
	                {
	                    int temp=numbers[j];
	                    numbers[j]=numbers[j+1];
	                    numbers[j+1]=temp;
	                }
	            }
	        }
	        System.out.println("从小到大排序后的结果是:");
	        for(i=0;i<numbers.length;i++)
	            System.out.print(numbers[i]+" ");
	    }
	

}
从小到大排序后的结果是:
1 2 3 4 5 8 9 

(2)选择排序

选择排序的基本思想:

选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n - 1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。  


每一趟在n-i+1(i=1,2,3…,n-1)个记录中选取关键字最小的记录与第i个记录交换,并作为有序序列中的第i个记录。

例如: 
待排序列: 43,65,4,23,6,98,2,65,7,79 
第一趟: 2,65,4,23,6,98,43,65,7,79 
第二趟: 2,4,65,23,6,98,43,65,7,79 
第三趟: 2,4,6,23,65,98,43,65,7,79 
第四趟: 2,4,6,7,43,65,98,65,23,79 
第五趟: 2,4,6,7,23,65,98,65,43,79 
第六趟: 2,4,6,7,23,43,98,65,65,79 
第七趟: 2,4,6,7,23,43,65,98,65,79 
第八趟: 2,4,6,7,23,43,65,65,98,79 
第九趟: 2,4,6,7,23,43,65,65,79,98

选择排序的时间复杂度为:O(n^2),空间复杂度:O(1)

代码实现:

package sort;
 
public class SelectSort {
	public static void main(String[] args) {
		//模拟数据
		int[] array = {52,63,14,59,68,35,8,67,45,99};
		System.out.println("原数组:");
		for (int i : array) {
			System.out.print(i+" ");
		}
		System.out.println();
		selectSort(array);
		System.out.println("排序后:");
		for (int i : array) {
			System.out.print(i+" ");
		}
	}
	
	public static void selectSort(int[] arr){
		for(int i = 0; i < arr.length-1; i++){
			int min = i;
			for(int j = i+1; j <arr.length ;j++){
				if(arr[j]<arr[min]){
					min = j;
				}
			}
			if(min!=i){
				swap(arr, i, min);
			}
		}
	}
	//完成数组两元素间交换
	public static void swap(int[] arr,int a,int b){
		int temp = arr[a];
		arr[a] = arr[b];
		arr[b] = temp;
	}
}
原数组:
52 63 14 59 68 35 8 67 45 99 
排序后:
8 14 35 45 52 59 63 67 68 99 

 

(3)插入排序


插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。 

代码实现:

package bubbleSort;

public class InsertSort {
	public static void main(String[] args){
		  int[] a = new int[]{43, 65, 4, 23, 6, 98, 2, 65, 7, 79};
		  System.out.println("插入排序:");
		    bInsertSort(a);
		    for(int i=0;i<10;i++){
		    	System.out.print(a[i]+"  ");
		    } 
	}

public static 	void bInsertSort(int a[]) //插入排序
	{
	    int low,high,mid;
	    int temp;
	    for(int i=1;i<a.length;i++)
	    {
	        low=0;
	        //把a[i]元素插入到它的前面a[0--(n-1)]中
	        temp=a[i];
	        high=i-1;
	        while(low<=high) //该while是折半优化算法,缩小a[i]的范围
	        {
	            mid=(low+high)/2;
	            if(a[mid]>temp)
	                high=mid-1;
	            else
	                low=mid+1;
	        }
	        int j=i;
	        while((j>low)&&(a[j-1]>temp)) //让a与已经排好的数组的各元素比较,小的放前面
	        {
	            a[j]=a[j-1];
	            --j;
	        }
	        a[low]=temp;
	    }
	}

}
插入排序:
2 4 6 7 23 43 65 65 79 98 
Program ended with exit code: 0

(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]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为5 3 3 4 3 8 9 10 11,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j] 交换的时刻。 

代码实现:Java

public class QuickSort {

#arr 需要排序的数组
#low 开始时最左边的索引=0
#high 开始时最右边的索引=arr.length-1
    public static void quickSort(int[] arr,int low,int high){
        int i,j,temp,t;
        if(low>high){
            return;
        }
        i=low;#左边哨兵的索引
        j=high;#右边哨兵的索引
        //temp就是基准位
        temp = arr[low];#以最左边为  基准位

        while (i<j) {
            //先看右边,依次往左递减
            #先从右往左找一个小于 基准位的数
            #当右边的哨兵位置所在的数>基准位的数 时
            #继续从右往左找(同时 j 索引-1)
            #找到后会跳出 while循环
            while (temp<=arr[j]&&i<j) {
                j--;
            }

            //再看左边,依次往右递增
            #步骤和上面类似
            while (temp>=arr[i]&&i<j) {
                i++;
            }

            //如果满足条件则交换
            if (i<j) {

#z、y 都是临时参数,用于存放 左右哨兵 所在位置的数据
                 int z = arr[i];
                 int y = arr[j];

                 # 左右哨兵 交换数据(互相持有对方的数据)
                 arr[i] = y;
                 arr[j] = z;

            }

        }

#这时 跳出了 “while (i<j) {}” 循环
#说明 i=j 左右在同一位置
        //最后将基准为与i和j相等位置的数字交换
         arr[low] = arr[i];#或 arr[low] = arr[j];
         arr[i] = temp;#或 arr[j] = temp;


#i=j
#这时  左半数组<(i或j所在索引的数)<右半数组
#也就是说(i或j所在索引的数)已经确定排序位置, 所以就不用再排序了,
# 只要用相同的方法 分别处理  左右数组就可以了

        //递归调用左半数组
        quickSort(arr, low, j-1);
        //递归调用右半数组
        quickSort(arr, j+1, high);
    }


    public static void main(String[] args){
        int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19};
        quickSort(arr, 0, arr.length-1);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}
输出为
1
2
2
3
4
4
7
7
8
9
10
19
62

 

(5)归并排序(分治法)


归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。 

详细图解:https://www.cnblogs.com/chengxiao/p/6194356.html

package sortdemo;

import java.util.Arrays;

/**
 * Created by chengxiao on 2016/12/8.
 */
public class MergeSort {
    public static void main(String []args){
        int []arr = {9,8,7,6,5,4,3,2,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void sort(int []arr){
        int []temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
        sort(arr,0,arr.length-1,temp);
    }
    private static void sort(int[] arr,int left,int right,int []temp){
        if(left<right){
            int mid = (left+right)/2;
            sort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
            sort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
            merge(arr,left,mid,right,temp);//将两个有序子数组合并操作
        }
    }
    private static void merge(int[] arr,int left,int mid,int right,int[] temp){
        int i = left;//左序列指针
        int j = mid+1;//右序列指针
        int t = 0;//临时数组指针
        while (i<=mid && j<=right){
            if(arr[i]<=arr[j]){
                temp[t++] = arr[i++];
            }else {
                temp[t++] = arr[j++];
            }
        }
        while(i<=mid){//将左边剩余元素填充进temp中
            temp[t++] = arr[i++];
        }
        while(j<=right){//将右序列剩余元素填充进temp中
            temp[t++] = arr[j++];
        }
        t = 0;
        //将temp中的元素全部拷贝到原数组中
        while(left <= right){
            arr[left++] = temp[t++];
        }
    }
}

 

(6)基数排序


基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。 

代码实现:

#include<iostream>
using namespace std;

//该函数的作用是找出num的pos位数的数字(比如:23的个位数数字是3)
int getNumPos(int num,int pos)
{
    int i;
    int temp=1;
    for(i=0;i<pos-1;i++)
        temp*=10;
    return (num/temp)%10;
}

void radixSort(int a[],int n) //基数排序
{
    int i,j,k,pos,num,index;
    //这几句话是创建一个从0-9(行)× (n+1)(列)的网格,第一列从上往下是0-9,第二列是该行包含的元素个数,默认为0个
    int *radixArrays[10];
    for(i=0;i<10;i++)
    {
        radixArrays[i]=(int *)malloc(sizeof(int)*(n+1));
        radixArrays[i][0]=0;
    }
    //pos最大为31为数,计算机能承受的最大范围了
    for(pos=1;pos<=31;pos++)
    {
        //该for循环是将数组的元素按照位数(pos)的值放进网格内
        for(i=0;i<n;i++)
        {
            num=getNumPos(a[i], pos);
            index=++radixArrays[num][0];
            radixArrays[num][index]=a[i];
        }
        //该for循环是将上面的for循环已经按照某个位数(pos)排列好的元素存入数组
        for(i=0,j=0;i<10;i++)
        {
            for(k=1;k<=radixArrays[i][0];k++)
                a[j++]=radixArrays[i][k];
            //清空网格,以便给下个位数排列
            radixArrays[i][0]=0;
        }
    }
}

int main()
{
    int a[10] = {43, 65, 4, 23, 6, 98, 2, 65, 7, 79};
    cout<<"基数排序:"<<endl;
    radixSort(a, 10);
    for(int i=0;i<10;i++)
        cout<<a[i]<<" ";
    cout<<endl;
    return 0;
}

执行结果:

基数排序:
2 4 6 7 23 43 65 65 79 98 
Program ended with exit code: 0

(7)希尔排序(shell)


希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小, 插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比O(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。 

详细图解:https://www.cnblogs.com/chengxiao/p/6104371.html

package DataStr;
 
import java.util.Arrays;
 
public class ShellSort {
	public static void main(String[] args) {
		int[] arr = SortTestHelper.getRandomArray(15, 0, 10);
		System.out.println("希尔排序前:"+Arrays.toString(arr));
		shellSort(arr);
		System.out.println("希尔排序后:"+Arrays.toString(arr));
	}
	
	/**
	 * 希尔排序
	 * @param arr 待排数组
	 */
	public static void shellSort(int[] arr) {
		for(int gap=arr.length/2; gap>0; gap/=2) { /*步长逐渐减小*/
			for(int i=gap; i<arr.length; i++) { /*在同一步长内*/
				//同一步长内排序方式是插入排序
				int temp = arr[i], j; //待排元素
				//j-gap代表有序数组中最大数的下标,j-pag表示有序数组的前一个元素,减pag是减去偏移量就是步长
				for(j=i; j>=gap && temp<arr[j-gap]; j-=gap)
					arr[j] = arr[j-gap]; //原有序数组最大的后移一位
				arr[j] = temp; //找到了合适的位置插入
			}
		}
	}
}

 

(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个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。

代码实现: 

package DataStr;
 
import java.util.Arrays;
 
/**
 * 数组下标0的元素也参与排序
 * @author stoneWang_L
 *
 */
public class HeapSort3 {
	public static void main(String[] args) {
		int[] arr = {312,126,272,226,28,165,123,8,12};
//		int[] arr = SortTestHelper.getRandomArray(20, 0, 2);
		System.out.println("堆排序前:"+Arrays.toString(arr));
		//堆排序
		heapSort(arr, arr.length);
		System.out.println("堆排序后:"+Arrays.toString(arr));
	}
	
	private static void heapSort(int[] arr, int count) {
		//完成构建大顶堆
		for(int i=(count-2)/2; i>=0; i--)
			shiftDown(arr, count, i);
		System.out.println("构建完成的大顶堆:"+Arrays.toString(arr));
		
		//将大顶堆头结点(最大数)与最后一个元素交换位置,然后除去最后这个最大的元素,再shiftDown操作维护该(除去最后一个元素的数组)堆为大顶堆。循环直到升序排序完成
		for(int j=count-1; j>0; j--) {
			swap(arr, 0, j);
			shiftDown(arr, j, 0);
		}
	}
	
	/**
	 * 
	 * @param arr 数组
	 * @param count	元素个数
	 * @param currentRoot 当前根节点的下标
	 */
	private static void shiftDown(int[] arr, int count ,int currentRoot) {
		while(2*currentRoot+1 < count) {
			int max = 2*currentRoot+1; //初始赋值left孩子
			if(max+1 < count && arr[max+1] > arr[max])
				max += 1;
			if(arr[currentRoot] >= arr[max])
				break;
			swap(arr, currentRoot, max);
			currentRoot = max;
		}
	}
	
	//交换
	public static void swap(int[] arr, int n, int m) {
		int temp = arr[n];
		arr[n] = arr[m];
		arr[m] = temp;
	}
}

执行结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&岁月不待人&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值