十大排序算法简记

在这里插入图片描述

  1. 选择排序( Selection )
    选择排序是最简单的,也是最没有用的,因为它的时间复杂度是 O( n^2 ) ,而且还不稳定。
    思路:
    从左到右扫描过去,找出最小的数的下标,跟这一趟的最开始数的下标index 交互位置,后面重复第一趟过程。
  2. 冒泡排序( Bubble )
    顾名思义,类似于冒泡的过程,它的时间复杂度是 O( n^2 ) ,稳定
    思路:
    从左到右扫描到 index,比较相邻两个数的大小,如果左边大于右边,则交换两值(大数就冒到右边了),index - - ;重复上述过程。
  3. 插入排序( Insertion )
    适用于基本有序的数组,它的时间复杂度是 O( n^2 ) ,稳定。
    思路:
    类似于扑克牌的插入排序;
static void insertion(int[] a){
		for (int i = 1; i < a.length; i++) {
			for (int j = i; j > 0; j--) {
				if (a[j] < a[j-1]) {
					swap(a, j, j-1);
				}
			}
		}
	}
  1. 快速排序( Quick )
    最常用高效的排序算法,它的时间复杂度是 O( n*log_2 n ) ,不稳定。
    思路:
    一定要先从右到左,找比轴小的值的下标high;再从左到右,找比轴大的值的下标low ;判断是否 high <= low ;不是的话就交换high和low ;一个回合结束后,交换high和key,然后递归左右半区;
	 static void quick(int[] a, int left, int right){
		 if (right<=left) {
			return;
		}
		 int i=left,j=right;
		 int key = a[left];//默认第一个为轴就行
		 while(i<j){
			 //从右到左找小于key的值
			 while(i<j && a[j]>=key){
				 j--;
			 }
			//从左到右找大于key的值
			 while(i<j && a[i]<=key){
				 i++;
			 }
			 if(i<j){
				 swap(a, i, j);
			 }else {
				break;
			}
		 }
		 swap(a, j, left);
		 //为什么右边一定要先行的原因
		 quick(a, left, j-1);
		 quick(a, j+1, right);
	 }
  1. 归并排序( Merge )
    Java对象排序默认使用的算法,它的时间复杂度是 O( n*log_2 n ) ,稳定。
    思路:
    将一个数组分成两半基本有序的数组(利用递归实现),再将两半有序的数组合并成有序的数组;怎么将两半基本有序的数组合并呢?
    定义两个指针分别指向两个数组的开始位置,比较大小,然后复制到新建的数组,最后将没移动完的数组全部复制到新的数组。
/**
*	通过递归实现一个数组分成两半基本有序
*/
static void MergeSort(int a[],int left, int right){
	    if (left==right) {
	        return;
	    }
	    //分成两半
	    int mid = left + (right-left)/2;
	    //左边排序
	    MergeSort(a,left,mid);
	    //右边排序
	    MergeSort(a,mid+1,right);
	    //合并数组
	    merge(a,left,mid+1,right);
	}

/**
*	将两个有序的数组合并
*/
static void merge(int a[], int leftPtr, int rightPtr, int rightBound){
	    int mid = rightPtr - 1;
	    int i = leftPtr;
	    int j = rightPtr;
	    int k = 0;
	    int[] temp = new int[rightBound - leftPtr + 1];
	    while ( i <= mid && j <= rightBound ) {
	        if (a[i] <= a[j]) {
	            temp[k++] = a[i++];
	        }else{
	            temp[k++] = a[j++];
	        }
	    }
	    while (i <= mid) {
	        temp[k++] = a[i++];
	    }
	    while (j <= rightBound) {
	        temp[k++] = a[j++];
	    }
	   for (int l = 0; l < temp.length; l++) {
		   a[ l + leftPtr] = temp[l];
	   }
	}
  1. 堆排序(Heap)
import java.util.Arrays;

/**
* @author Jackpon Huang
* @date 2020-07-28 21:32
* @desc 堆排序
* 思路:
*      一、构建顶堆
*      1. 先去第一个非叶子结点 (n/2 - 1),跟左右子节点比较,取大的下标
*      2. 如果largest下标跟刚开始的不一致,则交换并递归子堆
*
*      二、顶堆取值
*      
*/
public class HeapSort {
   public static void main(String []args){
       int []arr = {1,2,3,4,5};
       heapSort(arr,5);
       System.out.println(Arrays.toString(arr));
   }
   static void heapSort(int arr[], int n)
   {
       // 建立堆
       for (int i = n / 2 - 1; i >= 0; i--)
           heapify(arr, n, i);//arr 5 0

       // 一个个从堆顶取出元素
       for (int i=n-1; i>=0; i--)
       {
           swap(arr,0, i);
           heapify(arr, i, 0);
       }
   }
   static void heapify(int arr[], int n, int i)
   {
       int largest = i; // 将最大元素设置为堆顶元素
       int l = 2*i + 1; // left = 2*i + 1
       int r = 2*i + 2; // right = 2*i + 2

       // 如果 left 比 root 大的话
       if (l < n && arr[l] > arr[largest])
           largest = l;

       // I如果 right 比 root 大的话
       if (r < n && arr[r] > arr[largest])
           largest = r;

       if (largest != i)
       {
           swap(arr,i ,largest);

           // 递归地定义子堆
           heapify(arr, n, largest);
       }
   }
   static void swap(int []arr,int a ,int b){
       int temp=arr[a];
       arr[a] = arr[b];
       arr[b] = temp;
   }
}

  1. 希尔排序( Shell )
    优化的插入排序,希尔排序的发明使得我们突破了慢速排序时代,它的时间复杂度是O( n^1.3 ) ,不稳定。
    思路:
    希尔排序基于插入排序,它以一定的间隔(gap),并且是递减的,对数组进行插入排序,相较于插入排序,其移动次数更少,移动距离更短。
//二分
static void shell(int[] a){
		for (int gap = a.length/2; gap > 0; gap/=2) {
			for (int i = gap; i < a.length; i++) {
				for (int j = i; j > gap-1; j-=gap) {
					if (a[j] < a[j-gap]) {
						swap(a, j, j-gap);
					}
				}
			}
		}
	}

//Knuth序列
static void shell(int[] a){
		//Knuth序列效率要比二分高
		int h = 1;
		while (h <= a.length/3) {
			h = 3*h + 1;
		}
		for (int gap = h; gap > 0; gap=(gap+1)/3) {
			for (int i = gap; i < a.length; i++) {
				for (int j = i; j > gap-1; j-=gap) {
					if (a[j] < a[j-gap]) {
						swap(a, j, j-gap);
					}
				}
			}
		}
	}
  1. 桶排序( Bucket )

  2. 计数排序( Counting )
    桶思想的一种,非比较排序,适用于量大但范围小;它的时间复杂度是 O( n + k ) ,稳定。
    比如:某大型企业数万名员工年龄排序?如何快速得知高考名次?
    思路:
    遍历数组的值 x , 在 count [ x ] ++ ,再将count按出现的次数复制到原数组;

//version 1.0
static void counting(int[] a){
		int[] result = new int[a.length];
		int[] count = new int[61];
		for (int i = 0; i < a.length; i++) {
			count[ a[i] ]++;
		}
		
		int j=0;
		for (int i = 0; i < count.length; i++) {
			while (count[i]-- > 0) {
				result[j++] = i;
			}
		}
		for (int i = 0; i < result.length; i++) {
			a[i] = result[i];
		}
	}

到目前为止,计数排序version 1.0 有两大问题:其一,如果我们只要求排100~150,那么0到100的空间就会浪费;其二,算法不稳定。
对于空间的浪费,我们可以找出数组的最大和最小值;
对于算法的稳定性,解决的方法是累加数组count;

static void counting(int[] a){
		int max = a[0];
		int min = a[0];
		for (int i = 0; i < a.length; i++) {
			if (a[i] > max) {
				max = a[i];
			}
			if (a[i] < min) {
				min = a[i];
			}
		}
		int n = a.length;
		//这里k的大小是要排序的数组中,元素大小的极值差+1
		int k = max - min +1;
		int[] result = new int[n];
		int[] count = new int[k];
		for (int i = 0; i < n; i++) {
		//优化过的地方,减小了数组c的大小
			count[ a[i]-min ]++;
		}
		for (int i = 1; i < count.length; i++) {
			count[i] += count[i-1];
		}
		int j=0;
		for (int i = a.length-1; i >= 0; i--) {
			result[--count[a[i]-min] ] = a[i];
		}
		for (int i = 0; i < result.length; i++) {
			a[i] = result[i];
		}
	}
  1. 基数排序(Radix)
    桶思想的一种,非比较排序,它的时间复杂度是 O( n * k ) ,稳定。
    思路:
    将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
 static void radix(int[] a){
		 //找到数组a的最大值
		 int max = a[0];
		 for (int i = 0; i < a.length; i++) {
			if (a[i] > max) {
				max = a[i];
			}
		}
		 int[] result = new int[a.length];
		//按位数从低到高对数组进行排序
		 for (int exp = 1; max/exp > 0; exp *= 10) {
			 int[] count = new int[10];
			 
			//这里的思想其实跟计数排序是一样的
			 for (int i = 0; i < a.length; i++) {
				count[(a[i]/exp)%10]++;
			}
			//累加数组count
			 for (int i = 1; i < count.length; i++) {
				count[i] += count[i-1];
			}
			 
			 for (int i = a.length-1; i >= 0 ; i--) {
				result[--count[(a[i]/exp)%10]] = a[i];
			}
			 for (int i = 0; i < result.length; i++) {
				a[i] = result[i];
			}
		}
		 
	 }

学习资料来自马士兵的算法课程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值