排序算法总结(内排序)
Posted 我实在是想不出什么好听的昵称了啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法总结(内排序)相关的知识,希望对你有一定的参考价值。
1. 基本概念
1.1:什么是排序:
排序是计算机程序设计中的一项重要操作,其功能是指一个数据元素集合或序列重新排列成一个按数据元素某个数据项值有序的序列。
1.2 :排序的分类:(内排序和外排序)
1:内排序:指的是待排序列完全存放在内存中所进行的排序。其又可分为五大类排序:选择排序、插入排序、交换排序、归并排序和分配排序。
2:外排序:指的是排序过程中还需要访问外部存储器的排序。
2.(简单)选择排序
2.1:基本原理:
将待排序元素分为已排序(初始为空)和未排序两组,依次将未排序元素中值最小的元素放入到已排序的组别中。
2.2:排序基本过程:
- 在一组元素R[i]到R[n]中选择具有最小关键码的元素
- 若它不是这组元素中的第一个元素,则将它与这组元素中的第一个元素交换位置。
- 除去具有最小关键字的元素,在剩下的元素中重复第1.2步骤,直到剩余元素只有一个为止。
2.3:示例:
2.4:动态演示:
2.5:算法:
for(int i = 0; i < arr.length - 1; i++) //进行n-1趟排序
int k = i;
for(int j = k + 1; j < arr.length; j++)// 选最小的记录
if(arr[j] < arr[k])
k = j; //记下目前找到的最小值所在的位置
//元素交换
if(i != k)
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
2.6:算法分析:
- 无论初始状态如何,在第i趟排序中选择最小关键码的元素需要进行n-i次比较,则时间复杂度为O(n^2)
- 最好的情况:序列为正序时,移动次数为0,
- 最坏的情况:序列为反序时,每次排序都要执行交换操作,总移动次数为3(n-1)(最大值)
- 其排序是不稳定的
3.(直接)插入排序
3.1:基本原理:
每次将一个待排的元素,按其关键字大小插入到前面已经排号序的子文件的合适位置,直到全部记录插入完成为止。
3.2:排序基本过程:
将n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只包含有一个元素,无序表中有n-1个元素,将它的排序码依次跟有序表元素的排序码相比较,将它插入到有序表的适当位置,使之成为新的有序表。
3.3:示例:
3.4:动态演示:
3.5:算法:
public static void D-insertSort(int [] array)
for(int i=0;i<array.length;i++)
//有序[0,i)
//无序[i,array.length)
int j=i-1;
int key=array[i];
for(;j>=0 && key<array[j];j--)
array[j+1]=array[j];//元素后移
array[j+1]=key;//将key中的第i个元素插入到有序表中
3.6:算法分析:
- 空间角度:它只需要一个元素的辅助空间来用于元素的位置交换
- 时间角度:最外层循环需要n-1次插入操作,每次插入最少比较一次(正序),移动0次;最多比较i次,移动i+1次(逆序),则其时间复杂度为:O(n^2)
- 其是稳定的。
4.堆排序
4.1:基本原理:
4.2:排序基本过程:
- 根据初始输入数据形成初始堆
- 通过一系列的元素交换和重新调整堆进行排序
4.3:示例:(以大根堆为例)
4.4:动态演示:
4.5:算法:
/**
*
* @Title: buildHeap
* @Description: TODO(初始堆结构(完全二叉树的顺序结构))
* @param @param arr
* @param @return 参数
* @return int[] 返回类型
* @throws
*/
private int[] buildHeap(int[] arr)
//从最后一个节点array.length-1的父节点(array.length-1-1)/2开始,直到根节点0,反复调整堆
for(int i=(arr.length-2)/2;i>=0;i--)
setHeap(arr, i,arr.length);
return arr;
/**
*
* @Title: setHeap
* @Description: TODO(调整堆结构)
* @param @param arr
* @param @param t
* @param @param length 参数
* @return void 返回类型
* @throws
*/
private void setHeap(int[] arr,int t,int length)
int temp = arr[t];
for(int i=2*t+1; i<length-1; i=2*i+1) //i为初始化为节点k的左孩子,沿节点较大的子节点向下调整
if(i<length && arr[i]<arr[i+1]) //取节点较大的子节点的下标
i++; //如果节点的右孩子>左孩子,则取右孩子节点的下标
if(temp>=arr[i]) //根节点 >=左右孩子中关键字较大者,调整结束
break;
else //根节点 <左右孩子中关键字较大者
arr[t] = arr[i]; //将左右子结点中较大值array[i]调整到双亲节点上
t = i; //【关键】修改k值,以便继续向下调整
arr[t] = temp; //被调整的结点的值放人最终位置
/**
*
* @Title: heapSort
* @Description: TODO(进行堆排序)
* @param @param arr
* @param @return 参数
* @return int[] 返回类型
* @throws
*/
public int[] heapSort(int[] arr)
arr = buildHeap(arr); //初始建堆,array[0]为第一趟值最大的元素
for(int i=arr.length-1;i>1;i--)
int temp = arr[0]; //将堆顶元素和堆低元素交换,即得到当前最大元素正确的排序位置
arr[0] = arr[i];
arr[i] = temp;
setHeap(arr, 0,i); //整理,将剩余的元素整理成堆
return arr;
4.6:算法分析:
- 在整个堆排序中,需要进行Ln/2]+n-1次筛选运算,每次运算进行双亲和孩子或兄弟结点的排序码的比较和移动次数都不会超过完全二叉树的深度Llog2n]+1,所以,每次运算的时间复杂度为:
则整个堆排序过程时间复杂度为:: - 堆排序的时间复杂度与序列的初始状态无关,但是比较次数跟初始状态有关。
- 其空间复杂度为O(1),占用的辅助空间为1
- 其是不稳定的
5.归并排序
5.1:基本原理:
将两个有序表合并为一张有序表
5.2:排序基本过程:
顺序比较两个已经排序好的表的相应元素,小的移入到另一个表,重复上述步骤直到其中任意一个表都移动到另外一个表为止,
5.3:示例:
5.4:动态演示:
5.5:算法:
/**
*
* @Title: mergeSort
* @Description: TODO(归并排序)
* @param @param arr 待排数组
* @param @param left 左边
* @param @param right 右边
* @return void 返回类型
* @throws
*/
public static void mergeSort(int[] arr, int left, int right)
if(null == arr)
return;
if(left < right)
//找中间位置进行划分
int mid = (left+right)/2;
//对左子序列进行递归归并排序
mergeSort(arr, left, mid);
//对右子序列进行递归归并排序
mergeSort(arr, mid+1, right);
//进行归并
merge(arr, left, mid, right);
/**
*
* @Title: merge
* @Description: TODO(进行归并操作)
* @param @param arr
* @param @param left 左边
* @param @param mid 中间
* @param @param right 右边
* @return void 返回类型
* @throws
*/
private static void merge(int[] arr, int left, int mid, int right)
int[] tempArr = new int[arr.length];//临时数组
int leftStart = left;//左边开始下标
int rightStart = mid+1;//右边开始下标
int tempIndex = left;
while(leftStart <= mid && rightStart <= right)
if(arr[leftStart] < arr[rightStart])
tempArr[tempIndex++] = arr[leftStart++];
else
tempArr[tempIndex++] = arr[rightStart++];
while(leftStart <= mid)
tempArr[tempIndex++] = arr[leftStart++];
while(rightStart <= right)
tempArr[tempIndex++] = arr[rightStart++];
while(left <= right)
arr[left] = tempArr[left++];
5.6:算法分析:
- 利用二路归并排序时,需要利用和待排序数组相同的辅助数组作为临时单元,则该排序方法的空间复杂度为O(n)
- 其是稳定的
6.冒泡排序
6.1:基本原理:
通过对待排序序列从前往后,依次比较相邻元素的排序码,若发现逆序则交换,使排序码大的元素逐渐从前部移到尾部
6.2:排序基本过程:
6.3:动态演示:
6.4:算法:
/**
*
* @Title: BubbleSort
* @Description: TODO(冒泡排序)
* @param @param arr 待排数组
* @return void 返回类型
* @throws
*/
public static void BubbleSort(int[] arr)
int temp;//定义一个临时变量
for(int i=0;i<arr.length-1;i++)//冒泡趟数
for(int j=0;j<arr.length-i-1;j++)
if(arr[j+1]<arr[j])
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
6.5:算法分析:
- 若是正序,则只需要一趟排序,比较次数为(n-1)次,移动元素次数为0
- 若是逆序,则需进行n-1趟排序,比较次数为(n^2-n)/2,
移动次数为:3n^2-n)/2 - 其时间复杂度为O(n^2)
- 其是稳定的,只进行元素间的顺序移动
7.快速排序
7.1:基本原理:
任取待排序序列中的某个元素作为标准,通过一次划分,将待排元素分为左右两个子序列,左子序列元素的排序码都小于基准元素的排序码,右子序列元素的排序码都大于或等于基准元素的排序码,接着分别对两个子序列继续进行划分,直到每一个序列只有一个元素为止。
7.2:排序基本过程:
7.3:示例:
7.4:动态演示:
7.5:算法:
/**
*
* @Title: quickSort
* @Description: TODO(快速排序)
* @param @param arr 待排数组
* @param @param left 左下标
* @param @param right 右下标
* @return void 返回类型
* @throws
*/
public void quickSort(int[] arr,int left,int right)
if (left<right)
int temp=arr[left];//基准位
while (left<right)
//右边,依次向左递减
while (left<right && arr[right]>temp)
right--;
if (left<right)
arr[left++]=arr[right];
//左边,依次向右递增
while (left<right && arr[left]<=temp)
left++;
if (left<right)//元素交换
arr[right--]=arr[left];
arr[left]=temp;
quickSort(arr,left,temp-1);//递归调用左边部分
quickSort(arr,temp+1,right);//递归调用右边部分
7.6:算法分析:
8.希尔排序
8.1:基本原理:
先将整个待排元素序列分割为若干个子序列分别继续直接插入排序,待整个序列中的元素基本有序时再对整体元素进行一次直接插入排序。
8.2:示例:
8.3:动态演示:
8.4:算法:
public void sort()
int temp;
for (int k = array.length / 2; k > 0; k /= 2)
for (int i = k; i < array.length; i++)
for (int j = i; j >= k; j -= k)
if (array[j - k] > array[j])
temp = array[j - k];
array[j - k] = array[j];
array[j] = temp;
8.5:算法分析:
9.总结:
以上是关于排序算法总结(内排序)的主要内容,如果未能解决你的问题,请参考以下文章