参考: 小白都能看懂的七大排序算法详解
七种排序算法简单介绍:
(1)、冒泡排序:依次比较相邻的两个数,将小数放在前面,大数放在后面。
(2)、简单选择排序:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
(3)、直接插入排序:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
(4)、希尔排序:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
(5)、归并排序:典型的基于分治的递归算法。它不断地将原数组分成大小相等的两个子数组(可能相差1),最终当划分的子数组大小为1时,将划分的有序子数组合并成一个更大的有序数组
(6)、快速排序:选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素。
(7)、堆排序:堆排序是利用堆的性质进行的一种选择排序。利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。
思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。
public static void BubbleSort (int[] arr) {
for(int i=0;iarr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
思路:给定数组:int[] arr={里面n个数据};第1趟排序,在待排序数据arr[1]arr[n]中选出最小的数据,将它与arrr[1]交换;第2趟,在待排序数据arr[2]arr[n]中选出最小的数据,将它与r[2]交换;以此类推,第i趟在待排序数据arr[i]~arr[n]中选出最小的数据,将它与r[i]交换,直到全部排序完成。
public static void SelectionSort(int[] arr) {
//选择排序的优化
for(int i = 0; i < arr.length - 1; i++) {// 做第i趟排序
int k = i;
for(int j = k + 1; j < arr.length; j++){// 选最小的记录
if(arr[j] < arr[k]){
k = j; //记下目前找到的最小值所在的位置
}
}
//在内层循环结束,也就是找到本轮循环的最小的数以后,再进行交换
if(i != k){ //交换a[i]和a[k]
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
}
要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。
基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
public static void insertionSort(int[] a) {
int tmp;
for (int i = 1; i < a.length; i++) {
for (int j = i; j > 0; j--) {
if (a[j] < a[j - 1]) {
tmp = a[j - 1];
a[j - 1] = a[j];
a[j] = tmp;
}
}
}
}
希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminshing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
public static void shellSort(int[] arrays) {
//增量每次都/2
for (int step = arrays.length / 2; step > 0; step /= 2) {
//从增量那组开始进行插入排序,直至完毕
for (int i = step; i < arrays.length; i++) {
int j = i; int temp = arrays[j];
// j - step 就是代表与它同组隔壁的元素
while (j - step >= 0 && arrays[j - step] > temp) {
arrays[j] = arrays[j - step];
j = j - step;
}
arrays[j] = temp;
}
}
}
原理:典型的基于分治的递归算法。它不断地将原数组分成大小相等的两个子数组(可能相差1),最终当划分的子数组大小为1时,将划分的有序子数组合并成一个更大的有序数组
思路:一个是分,也就是把原数组划分成两个子数组的过程。另一个是治,它将两个有序数组合并成一个更大的有序数组。它将数组平均分成两部分: center = (left + right)/2,当数组分得足够小时—数组中只有一个元素时,只有一个元素的数组自然而然地就可以视为是有序的,此时就可以进行合并操作了。因此,上面讲的合并两个有序的子数组,是从 只有一个元素 的两个子数组开始合并的。
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
算法的实现可分为以下几步:
在数组中选一个基准数(通常为数组第一个);
将数组中小于基准数的数据移到基准数左边,大于基准数的移到右边;
对于基准数左、右两边的数组,不断重复以上两个过程,直到每个子集只有一个元素,即为全部有序。
可能描述得有些抽象,接下来用图一步一步的示意:
将数组第一个数23赋给temp变量,指针 i 指向数组第一个元素,指针 j 指向数组最后一个元素
从 j 开始遍历(从右往左),遇到13时,因为13<=temp,因此将arr[j]填入arr[i]中,即此时指针 i 指向的数为13;
再从 i 遍历(从左往右),遇到45时,因为45>temp,因此将arr[i]填入arr[j]中,此时指针 j 指向的数为45;
继续从 j 遍历,遇到11时,因为11<=temp,因此将arr[j]填入arr[i]中,即此时指针 i 指向的数为11;
从 i 遍历,遇到89时,因为89>temp,因此将arr[i]填入arr[j]中,此时指针 j 指向的数为89;
从 j 遍历,遇到17时,因为17<=temp,因此将arr[j]填入arr[i]中,即此时指针 i 指向的数为17;
从 i 遍历,遇到72时,因为72>temp,因此将arr[i]填入arr[j]中,此时指针 j 指向的数为72;
从 j 遍历,遇到3时,因为3<=temp,因此将arr[j]填入arr[i]中,即此时指针 i 指向的数为3;
从 i 遍历,遇到26时,因为26>temp,因此将arr[i]填入arr[j]中,此时指针 j 指向的数为26;
此时完成算法的第2个步骤,接下来将23左边和右边的子区间分别用以上方法进行排序,直到区间只有一个元素即排序完成。
//快速排序算法(从小到大)
//arr:需要排序的数组,begin:需要排序的区间左边界,end:需要排序的区间的右边界
void quickSort(int *arr,int begin,int end)
{
//如果区间不只一个数
if(begin < end)
{
int temp = arr[begin]; //将区间的第一个数作为基准数
int i = begin; //从左到右进行查找时的“指针”,指示当前左位置
int j = end; //从右到左进行查找时的“指针”,指示当前右位置
//不重复遍历
while(i < j)
{
//当右边的数大于基准数时,略过,继续向左查找
//不满足条件时跳出循环,此时的j对应的元素是小于基准元素的
while(i temp)
j--;
//将右边小于等于基准元素的数填入右边相应位置
arr[i] = arr[j];
//当左边的数小于等于基准数时,略过,继续向右查找
//(重复的基准元素集合到左区间)
//不满足条件时跳出循环,此时的i对应的元素是大于等于基准元素的
while(i
基本思想:(升序用大根堆,降序就用小根堆)
1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组
//堆排序
public static void heapSort(int[] arr) {
//构造大根堆
heapInsert(arr);
int size = arr.length;
while (size > 1) {
//固定最大值
swap(arr, 0, size - 1);
size--;
//构造大根堆
heapify(arr, 0, size);
}
}
//构造大根堆(通过新插入的数上升)
public static void heapInsert(int[] arr) {
for (int i = 0; i < arr.length; i++) {
//当前插入的索引
int currentIndex = i;
//父结点索引
int fatherIndex = (currentIndex - 1) / 2;
//如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
//然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
while (arr[currentIndex] > arr[fatherIndex]) {
//交换当前结点与父结点的值
swap(arr, currentIndex, fatherIndex);
//将当前索引指向父索引
currentIndex = fatherIndex;
//重新计算当前索引的父索引
fatherIndex = (currentIndex - 1) / 2;
}
}
}
//将剩余的数构造成大根堆(通过顶端的数下降)
public static void heapify(int[] arr, int index, int size) {
int left = 2 * index + 1;
int right = 2 * index + 2;
while (left < size) {
int largestIndex;
//判断孩子中较大的值的索引(要确保右孩子在size范围之内)
if (arr[left] < arr[right] && right < size) {
largestIndex = right;
} else {
largestIndex = left;
}
//比较父结点的值与孩子中较大的值,并确定最大值的索引
if (arr[index] > arr[largestIndex]) {
largestIndex = index;
}
//如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环
if (index == largestIndex) {
break;
}
//父结点不是最大值,与孩子中较大的值交换
swap(arr, largestIndex, index);
//将索引指向孩子中较大的值的索引
index = largestIndex;
//重新计算交换之后的孩子的索引
left = 2 * index + 1;
right = 2 * index + 2;
}
}
//交换数组中两个元素的值
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}