常用排序算法与实现

1. 插入排序
 1.1 直接插入排序
 1.2 希尔排序
2. 选择排序
 2.1 简单选择排序
 2.2 堆排序
3. 交换排序
 3.1 冒泡排序
 3.2 快速排序
4. 归并排序
5. 基数排序

希尔排序,堆排序,基数排序个人觉得比较难,本文中只涉及概念,笔记单独写

1. 插入排序

1.1 直接插入排序

基本思想

将数组分为有序与无序两部分,初始时,有序部分只包含第一个元素,其后元素视为无序数组,取出无序数组的第一个元素,插入到有序数组的相应位置。

排序过程

以数列{3,5,4,2,1}为例 : 】前的就是有序数列
初始,将数组分为有序与无序两部分 3 】 5 4 2 1
第一次:无序数组第一位为5,比3大,插入到3之后 3 5 】 4 2 1
第二次:无序数组第一位为4,比3大,比5小,插入之间 3 4 5 】 2 1
第三次:无需数组第一位为2,比3小,插入到3之前 2 3 4 5 】 1
第四次:无序数组第一位为1,比2小,插入到2之前 1 2 3 4 5 】
排序完成,最终顺序{1,2,3,4,5}

代码实现
	public static int[] sort(int[] arr){
		int j = 0;
		//arr[0]视为有序数组,所以外层循环从1开始
		for (int i = 1; i < arr.length; i++) {
			int insert = arr[i];//要插的数据
			for (j = i; j > 0 && insert < arr[j-1]; j--) {
				//从后往前插,大的数依次后移
				arr[j] = arr[j-1];
			}
			arr[j] = insert;
		}
		return arr;
	}

返回目录

1.2 希尔排序

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1)插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
2)插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

返回目录



2. 选择排序

2.1 简单选择排序

简单选择排序,又叫直接选择排序。

基本思想

每一趟从待排序的数据元素中选择出最小(或最大)的一个元素,顺序放在已排好的数列后,直到全部待排序数据元素排完。

排序过程

以数列{4,3,5,2,1}为例 : 】前的就是已经排序的有序数列
第一次:最小值为1,与第一个交换 1 】 4 3 5 2
第二次:最小值为2,与第二个交换 1 2 】 4 3 5
第三次:最小值为3,与第三个交换 1 2 3 】 4 5
第四次:最小值为4,无需交换 1 2 3 4 】 5
(最后一位已经确定最大,无需下一次排序,所以交换次数是arr.length-1
排序完成,最终顺序{1,2,3,4,5}

代码实现
	static int[] sort(int[] arr){
		int minIndex;
		for (int i = 0; i < arr.length-1; i++) {
			minIndex = i;//假设每轮外循环时,第一个为最小的数
			for (int j = i+1; j < arr.length; j++) {
				if(arr[j] < arr[minIndex]){//找到最小数
					minIndex = j;//最小索引
				}
			}
			int temp = arr[i];
			arr[i] = arr[minIndex];
			arr[minIndex] = temp;//最小值换到最前
		}
		return arr;
	}

返回目录

2.2 堆排序

堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

返回目录



3. 交换排序

3.1 冒泡排序

基本思想

依次将相邻的两数据进行比较,如果比后面的数据大就往上冒。一趟一趟的比较,比如,第一趟:先比较第1个与第2个数,大的往后放,再比较第2个与第3个数,大的往后放…第一趟比完之后,最后一位必定是最大的;第二趟,再次两两比较,但是比较到第n-2与n-1就停止,因为最后一位已经确定是最大;第三趟,最后的两位确定,比较到n-3与n-2停止。
PS:冒泡排序最简单最容易理解,但是比较慢,实际场景中不推荐使用

排序过程

以数列{3,1,5,4,2}为例,【后为已经比较完的,[ ]中为正要两两比较的数
第一趟:
[1 3] 5 4 2 3大于1,交换
1 [3 5] 4 2 3小于5,不交换
1 3 [4 5] 2 5大于4,交换
1 3 4 [2 5] 5大于2,交换
第一趟结束,最后一位固定:5,即1 3 4 2 【5
第二趟:
[1 3] 4 2 【5 1小于3,不交换
1 [3 4] 2 【5 3小于4,不交换
1 3 [2 4] 【5 4大于2,交换
第二趟结束,后两位固定:4,5,即1 3 2 【4 5
……
后面几趟同理,直到排完

代码实现
	public static void sort(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			//因为比较的是arr[j]与arr[j+1],所以减1
			//因为第i趟比较完后,arr[arr.length-i]后的为有序
			for (int j = 0; j < arr.length-i-1; j++) {
				//把大的数往上冒
				if(arr[j] > arr[j+1]){
					int temp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = temp;
				}
			}
		}
	}

返回目录

3.2 快速排序

基本思想

快排是冒泡排序的一种改进。
一般步骤:
1)选择一个基准值
2)根据基准值,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小
3)然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

排序过程

快速排序有三种实现方式——

快速排序总代码
	static void quickSort(int[] arr,int low,int high){
		//如果low=high,则说明数组只有一个元素,无需再处理
		//如果low>high,则说明上次中枢元素的位置就是low或high,这种情况下分区不存,也不用处理
		if(low<high){
			//对分区进行排序整理
			int centerIndex = part1(arr, low, high);//调用对应的part1/2/3三种实现
			//对中枢元素左右的数组快速排序递归
			//以centerIndex为界,分为三部分[low, centerIndex-1],[centerIndex],[centerIndex+1, high]
			quickSort(arr, low, centerIndex-1);
			quickSort(arr, centerIndex+1, high);
		}
	}
1. 实现一

初始化时,border指针指向中枢元素(用以分隔前后元素),low指针指向中枢元素后的元素,high指针指向最后一位元素。
整个过程,high指针不动,只向尾移动low指针,low指针找到比中枢元素小的元素,并与border指针指向的下一元素交换,然后border指向这个小的元素,low继续后移,直到low与high重合为止。
过程示意图:以{5,2,6,4,3,8,1,7}为例
快速排序一

快速排序实现代码(一)
	static int part1(int[] arr,int low,int high){
		int border = low;
		int center = arr[low];//以第一个元素为中枢元素
		//通过i来移动,这里的i相当于上文的low,这里的low则用于确定数组边界
		for (int i = low+1; i <= high; i++) {
			if(arr[i] < center){
				border++;
				int temp = arr[i];
				arr[i] = arr[border];
				arr[border] = temp;
			}
		}
		//如果border没有移动,则说明后面的元素都比中枢元素大
		//如果border移到high位置,则说明所有元素小于中枢元素
		int temp = arr[border];
		arr[border] = arr[low];
		arr[low]  = temp;
		return border;
	}
2. 实现二

如果low指针不是指向中枢元素,则使用low指针找到比中枢元素大的元素,找到后与high指针指向元素交换;
如果high指针不是指向中枢元素,则使用high指针找到比中枢元素小的元素,找到后与low指针指向元素交换。
过程示意图:以{5,2,6,4,3,8,1,7}为例,low初始时指向中枢元素
快速排序二

快速排序实现代码(二)
	static int part2(int[] arr,int low,int high){
		int centerIndex = low;//以第一个元素为中枢元素
		do {
			if(centerIndex != high){//low指针指向中枢元素时,移动high指针
				//如果high指针指向元素小于中枢元素,则交换位置
				if(arr[high] < arr[centerIndex]){
					int temp = arr[high];
					arr[high] = arr[centerIndex];
					arr[centerIndex] = temp;
					centerIndex = high;//中枢元素换到high指针处
				}else{//不小于则指针前移继续找
					high--;
				}
			}else{//high指针指向中枢元素时,移动low指针
				//与上个if语句同理
				if(arr[low] > arr[centerIndex]){
					int temp = arr[low];
					arr[low] = arr[centerIndex];
					arr[centerIndex] = temp;
					centerIndex = low;
				}else{
					low++;
				}
			}
		} while (low != high);//退出条件为low=high,即两指针重合
		
		return centerIndex;
	}
3. 实现三

中枢元素先不移动,但是low指针与high指针移动,low找大于中枢元素的元素,high找小于中枢元素的元素,然后交换low与high的元素,直到low=high为止,最后将中枢元素与前半部分数组的最后一个元素交换。
过程示意图:以{5,2,6,4,3,8,1,7}为例
快速排序三

快速排序实现代码(三)
	static int part3(int[] arr,int low,int high){
		int centerIndex = low;//以第一个元素为中枢元素
		int center = arr[centerIndex];
		low++;//low指向中枢元素后第一个元素
		do {
			//high与low是两个不同步的循环,所以使用了两个while
			//如果high指针未超出low指针
			while(low<high){
				if(arr[low] >= center){
					break;//找到大于等于中枢元素的元素
				}else{
					low++;
				}
			}
			//如果high指针未超出low指针
			while(low<high){
				if(arr[high] <= center){
					break;//找到小于等于中枢元素的元素
				}else{
					high--;
				}
			}
			int temp = arr[low];
			arr[low] = arr[high];
			arr[high] = temp;
		} while (low != high);//退出条件为low与high重合
		
		//如果low与high指针的元素小于中枢元素,则与arr[low]交换,否则与arr[low-1]交换
		if(center > arr[low]){
			int temp = arr[low];
			arr[low] = arr[centerIndex];
			arr[centerIndex] = temp;
			centerIndex = low;
		}else{
			int temp = arr[low-1];
			arr[low-1] = arr[centerIndex];
			arr[centerIndex] = temp;
			centerIndex = low-1;
		}
		return centerIndex;
	}

返回目录



4. 归并排序

基本思想

分治法的思想。
分:归——递归拆分,将数组拆成更小的数组,直至数组只包含一个元素。
治:并——合并,将小数组两两合并,合并时排序。

排序过程

以{5,3,9,7,1,8,2,6,4}为例
先将数组依次从中间位置拆开为两个更小的数组,直至拆到只剩一个元素
归并-分
依次将小数组组合,组合的同时排序
归并-治
关于合并的具体实现: 以{1,3,5,7,9}与{2,4,6,8}为例
合并的步骤

代码实现
排序总代码实现
	/**
	 * 归并排序的函数入口
	 * @param arr
	 * @param start
	 * @param end
	 */
	public static void sort(int[] arr,int start,int end){
		partition(arr,start,end);
	}
分:将数组从中间位置一份为二
	/**
	 * 递归划分数组
	 * @param arr
	 * @param start
	 * @param end
	 */
	private static  void partition(int[] arr,int start,int end){
		//递归调用的退出条件为start大于等于end
		if(start < end){
			int mid = (start+end)/2;//数组的中间位置
			//数组分为[start,mid]与[mid+1,end]两部分
			//递归调用,不断将数组对半分
			partition(arr,start,mid);
			partition(arr,mid+1,end);
			//合并划分后的数组
			merge(arr, start, end, mid);
		}
	}
合:将两个小数组合并,且排序
	/**
	 * 合并数组,在合并过程中对数组排序
	 * @param arr
	 * @param start
	 * @param end
	 * @param mid
	 */
	private static void merge(int[] arr,int start,int end,int mid){
		int i = start;//左序数组的指针
		int j = mid+1;//右序数组的指针
		int t = 0;//临时数组的指针
		int[] temp = new int[arr.length];//临时数组,用于存放排序合并后的数列
		while(i<=mid && j<=end){
			//比较,将两个指针指向的数中较小的那个放入临时数组
			if(arr[i] <= arr[j]){
				temp[t] = arr[i];
				t++;
				i++;
			}else if(arr[i] > arr[j]){
				temp[t] = arr[j];
				t++;
				j++;
			}
		}
		//如果两两比较完后,左边还有剩余元素,则把左边剩余填入临时数组
		while(i <= mid){
			temp[t] = arr[i];
			t++;
			i++;
		}
		//同理,填入右边剩余元素
		while(j <= end){
			temp[t] = arr[j];
			t++;
			j++;
		}
		//将临时数组的元素全部拷贝回原数组
		t = 0;//从头拷贝,所以t先归0
		while(start <= end){
			arr[start] = temp[t];
			start++;//当start加到不小于end时完成拷贝
			t++;
		}
	}

返回目录



5. 基数排序

基数排序(radixsort)则是属于“分配式排序”(distributionsort),基数排序法又称“桶子法”(bucketsort)或binsort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O(nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。

返回目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值