数据结构与算法——八种基本排序算法的学习总结与实现

在数据结构与算法的学习中,排序算法有着举足轻重的地位,其实自己之前也学过,但是由于时间较长,也忘得差不多了,也才想着趁着假期的时间来系统的学习一下。但是理解的不够细致,还需要在花费点时间。
在这里插入图片描述

八种基本排序算法的实现

冒泡排序

冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

时间复杂度: O(n^2)

步骤如下:
比较相邻的元素。如果第一个比第二个大,就交换他们两个,直到把最大的元素放到数组尾部。
遍历长度减一,对剩下的元素从头重复以上的步骤。
直到没有任何一对数字需要比较时完成。

冒泡排序的java实现:

package demo4;
import java.util.Arrays;

public class BubbleSort {
	public static void main(String[] args) {
		int [] arr=new int[]{5,7,2,9,4,1,0,5,7};
		System.out.println(Arrays.toString(arr));
		bubbleSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	/*
	 * 冒泡排序
	 * 共需要比较length-1次
	 */
	public static void bubbleSort(int[] arr) {
		//控制共比较多少轮
		for(int i=0;i<arr.length-1;i++) {
			//控制比较次数
			for(int j=0;j<arr.length-1;j++) {
				if(arr[j]>arr[j+1]) {
					int temp=arr[j];
					arr[j]=arr[j+1];
					arr[j+1]=temp;
				}
			}
		}
	}
}

快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

时间复杂度:O(N*logN)

步骤:
从数列中挑出一个元素,称为 “基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

快速排序的java代码实现:

package demo4;

import java.util.Arrays;

public class QuickSort {
	public static void main(String[] args) {
		int [] arr=new int[]{5,7,2,9,4,1,0,5,7};
		quickSort(arr,0,arr.length-1);
		System.out.println(Arrays.toString(arr));
		
	}
	/*
	 * 快速排序
	 */
	public static void quickSort(int [] arr,int start,int end) {
		if(start<end) {
			//把数组中第0 个数作为基准数
			int stard=arr[start];
			//记录需要排序的下标
			int low=start;
			int high=end;
			//循环找比标准数大的数和比标准数小的数
			while(low<high) {
				//右边的数字比标准数大
			while(low<high&&stard<=arr[high]) {
				high--;
				}
			//使用右边的数字替换左边的数字
			arr[low]=arr[high];
			//如果左边的数字比标准数小
			while(low<high&&arr[low]<=stard) {
				low++;
				
			}
			arr[high]=arr[low];
			}
			//把标准数赋给所在的位置的元素
			arr[low]=stard;
			//处理所有的小的数字
			quickSort(arr,start,low);
			//处理所有大的数字
			quickSort(arr,low+1,end);
		}
	}		
}

插入排序(直接插入)

插入排序(Insertion Sort)在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

时间复杂度: O(n^2)

步骤如下:
①从第一个元素开始,该元素可以认为已经被排序
②取出下一个元素,在已经排序的元素序列中从后向前扫描
③如果该元素(已排序)大于新元素,将该元素移到下一位置
④重复步骤3,直到找到已排序的元素小于或者等于新元素的位置5. 将新元素插入到该位置中
⑤重复步骤2
插入排序的java代码实现:

package demo4;

import java.util.Arrays;

public class insertSort {
	public static void main(String[] args) {
		int [] arr=new int[] {5,2,3,6,1,8,7};
		insertSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void insertSort(int[] arr) {
		//遍历所有的数字
		for(int i=1;i<arr.length;i++) {
			//如果当前数字比前一个数字更小
			if(arr[i]<arr[i-1]) {
				//把当前边遍历数字存起来
				int temp=arr[i];
				int j;
				//遍历当前数字前面的所有数字
				for(j=i-1;j>=0&&temp<arr[j];j--) {
					//把前一个数字赋值给后一个数字
					arr[j+1]=arr[j];
				}
				//把临时变量(外层for循环的元素)赋值给不满足条件的后一个元素
				arr[j+1]=temp;
			}	
		}
	}
}

插入排序之希尔排序

针对直接插入排序的效率问题,有人对此进行了改进与升级,这就是现在的希尔排序。希尔排序又称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是基于插入排序的以下两点性质而提出改进方法的:

插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
希尔排序的java实现:

package demo4;

import java.util.Arrays;

public class shellSort {
	public static void main(String[]args) {
		int [] arr=new int[] {5,2,3,6,1,8,7,0};
		System.out.println(Arrays.toString(arr));
		shellSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	public static void shellSort(int [] arr) {
		int k=1;
		//遍历所有的步长
		for(int d=arr.length/2;d>0;d/=2) {
			//遍历所有元素
			for(int i=0 ;i<arr.length;i++) {
				//遍历本组中所有的元素
				for(int j=i-d;j>=0;j-=d) {
					if(arr[j]>arr[j+d]) {
						int temp=arr[j];
						arr[j]=arr[j+d];
						arr[j+d]=temp;
					}
				}
			}
			System.out.println("第"+k+"次排序结果:"+Arrays.toString(arr));
			k++;
		}
	}
}

排序后的结果为:
在这里插入图片描述

选择排序

选择排序法的思路是:
1、找出一个最小数,交换到最前面。
2、在剩下的数里面,再找一个最小的,交换到剩下数的最前面
3、重复步骤2 ,直到所有数都已排好。
显然,对于含有N个数的数组来说,其过程也要进行N-1趟 ( 0 <= i < N-1 )。
找出一个最小数,交换到最前面的方法是:
先将剩下数中的第一个数(序号是i)作为基数,用变量k记下其序号,后面的数依次与该基数比较,若比基数还小,则用k记下其序号(注意:此时不要交换),当所有数都与基数比较后,k中存放的就是最小数的序号,然后将它交换到最前面(现在才交换)。在上面的过程中,数据只交换了一次,即每趟只交换一次数据。
选择排序的java实现:

package demo4;

import java.util.Arrays;

public class selectSort {
	public void main(String[]args) {
	int [] arr=new int[] {6,1,4,3,6,1,8,7,0};
	selectSort(arr);
	System.out.println(Arrays.toString(arr));
	}
	/*
	 * 选择排序
	 */
	public static void selectSort(int []arr) {
		//遍历所有的数
		for(int i=0;i<arr.length;i++) {
			int minIndex=i;
			//把当前遍历的数和后面所有的数依次进行比较
			for(int j=0;j<arr.length;j++) {
				//如果后面比较的数比记录的最小的数小
				if(minIndex<arr[j]) {
					//记录下最小的那个数的下标
					minIndex=j;
				}
			}
			//如果最小的数和当前遍历的数的下标不一致,说明下标为Index的数比当前遍历的数更小
			if(i!=minIndex) {
				int temp=arr[i];
				arr[i]=arr[minIndex];
				arr[minIndex]=temp;
			}
		}
	}
}

归并排序

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

时间复杂度:O(N*logN)

步骤如下:
申请空间,创建两个数组,长度分别为两个有序数组的长度
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针达到序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
归并排序的java代码实现:

package demo4;
import java.util.Arrays;

public class MergeSort {
	public static void main(String[]args) {
		int [] arr=new int[] {1,3,5,2,4,6,8,10};
		System.out.println(Arrays.toString(arr));
		mergeSort(arr,0,arr.length-1);
		System.out.println(Arrays.toString(arr));
	}
	/*
	 * 归并排序
	 */
	public static void mergeSort(int[]arr,int low,int high) {
		int middle=(high+low)/2;
		if(low<high) {
		//处理左边
		mergeSort(arr,low,middle);
		//处理右边
		mergeSort(arr,middle+1,high);
		//归并
		merge(arr,low,middle,high);
		}
	}
	
	public static void merge(int [] arr,int low,int middle,int high) {
		//用于存储归并后的临时数组
		int [] temp=new int[high-low+1];
		//记录第一个数组中需要遍历的数组
		int i=low;
		//记录第二个数组中需要遍历的数组
		int j=middle+1;
		//用于记录在临时数组中存放的下标值
		int index=0;
		//遍历两个数组,取出小的数组放入临时文件中
		while(i<=middle&&j<=high) {
			//第一个数组中的数据较小
			if(arr[i]<=arr[j]) {
				//把小的数据放到临时数组中
				temp[index]=arr[i];
				//让下标下移一位
				i++;
			}else {
				temp[index]=arr[j];
				j++;
			}
			index++;
		}
		//处理多余的数据
		while(j<=high) {
			temp[index]=arr[j];
			j++;
			index++;
		}
		while(i<=middle) {
			temp[index]=arr[i];
			i++;
			index++;
		}
		//把临时数组中的数据重新加入原数组
		for(int k=0;k<temp.length;k++) {
			arr[k+low]=temp[k];
		}
	}
}

基数排序

基数排序(英语:Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(Tabulation Machine)上的贡献[1]。

它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。
基数排序的java代码实现:

package demo4;

import java.util.Arrays;

public class RadixSort {
	public static void main(String[]args) {
		int [] arr=new int[] {23,6,189,45,278,56,78,980,51};
		System.out.println(Arrays.toString(arr));
		radixSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void radixSort(int [] arr) {
		//存储素数组中最大的数字
		int max=Integer.MIN_VALUE;
		for(int i=0;i<arr.length ;i++) {
			if(arr[i]>max) {
				max=arr[i];
			}
		}
		//System.out.println(max);
		//求最大数的位数
		int maxLength=(max+"").length();
		//用来临时存储数据的数组
		int[][] temp=new int [10][arr.length];
		//用于记录在temp中相应的数组中存放的数组
		int[] counts=new int[10];
		
		//根据最大长度的数决定比较的次数
		for(int i=0,n=1;i<maxLength;i++,n*=10) {
			//把一个数分别计算余数
			for(int j=0;j<arr.length;j++) {
				//计算余数
				int ys=arr[j]/n%10;
				temp[ys][counts[ys]]=arr[j];
				//记录数量
				counts[ys]++;
			}
			//记录取的元素要放的位置
			int index=0;
			//把数字取出来
			for(int k=0;k<counts.length;k++) {
				//记录数量的数组中当前余数记录的数量部位0
				if(counts[k]!=0) {
					//循环取出数组
					for(int l=0;l<counts[k];l++) {
						//取出元素
						arr[index]=temp[k][l];
						//记录下一个位置
						index++;
					}
					//把数量置为0
					counts[k]=0;
				}
				
			}

		}
	}	
}

堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]]>=A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

堆排序的java代码实现:

package demo4;

import java.util.Arrays;

public class HeapSort {
	public static void main(String[] args) {
		int [] arr=new int[] {9,6,8,7,0,1,10,4,2};
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}

	/*
	 * 堆排序
	 */
	public static void heapSort(int []arr) {
		//开始位置是最后一个非叶子节点,即最后一个节点的父节点
		int start=(arr.length-1)/2;
		
		for(int i=start;i>=0;i--) {
			maxheap(arr,arr.length,i);
		}
		//把数组中的第0 个和堆中的最后一个交换位置,再把前面的处理为大顶堆
		for(int i=arr.length-1;i>0;i--) {
			int temp=arr[0];
			arr[0]=arr[i];
			arr[i]=temp;
			maxheap(arr,i,0);
			
		}
		
	}
	public static void maxheap(int [] arr,int size,int index) {
		//左子节点
		int leftNode=2*index+1;
		//右子节点
		int rightNode=2*index+2;
		int max=index;
		//和两个子节点进行对比,找出最大的节点
		if(leftNode<size && arr[leftNode]>arr[max]) {
			max=leftNode;	
		}
		if(rightNode<size && arr[rightNode]>arr[max]) {
			max=rightNode;
		}
		//交换位置
		if(max!=index) {
			int temp=arr[index];
			arr[index]=arr[max];
			arr[max]=temp;
			//交换位置后需要重新调整位置
			maxheap(arr,size,max);
		}
	}
}

排序算法的比较

在这里插入图片描述
在以上这些排序算法的学习中,自己花费了太多的时间。可是最终理解得还是不够透彻,以上算法采用了比较基础的方法实现,考虑的不是很全面,自己也会在之后的学习中多加思索。

发布了22 篇原创文章 · 获赞 4 · 访问量 1509

猜你喜欢

转载自blog.csdn.net/qq_42711899/article/details/104329482
今日推荐