从第2位(1位)开始,如果这一位的值小于上一位,则从这一位开始往前遍历找到适合插入的位置,插入进去,其插入点的后面部分均向后移一位。
排序方法 | 平均时间 | 最快时间 | 最慢时间 | 辅助存储 | 稳定性 |
---|---|---|---|---|---|
直接插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
public static void insertSort(int[] array){
for(int i=1;i<array.length;i++){
int j=i;
int target=array[i];//表示想插入的数据
while(j > 0 && target<array[j-1]){//如果插入的数据小于数组的前一个时
array[j]=array[j-1];
j--;
}
array[j]=target;
}
}
数据量大的时候效率很低,直接插入排序适用数据量小的情况。
根据步长排序,每一轮的在该步长的数据都会变为顺序,比如步长为3,则0,3,6,9,12下标的数据,单拎出来是顺序的。然后1,4,7,10,13也一样。
步长会根据一个算法逐渐变为1。
使用while(j>step-1 && target
排序方法 | 平均时间 | 最快时间 | 最慢时间 | 辅助存储 | 稳定性 |
---|---|---|---|---|---|
希尔排序 | O(n^1.3) | O(n) | O(n^2) | O(1) | 不稳定 |
正序效率最高,反序效率最低。
public static void shellSort(int[] array,int step){
while(step > 1){
for(int k=0;k<step;k++) {//对步长的定位,选择每次操作的开始位置
for(int i=k+step;i<array.length;i=i+step){//i表示从第2个数开始插入
int j=i;
int target=array[i];//表示想插入的数据
while(j>step-1 && target<array[j-step]){//如果插入的数据小于数组的前一个时
array[j]=array[j-step];
j=j-step;
}
array[j]=target;
}
}
step = step/5 + 1;//这一轮的step全部排序完之后,改变步长继续操作
}
}
双层嵌套循环,每一次都找剩下集合中最小的一个,找到之后与外层循环到的位置的元素互换。
排序方法 | 平均时间 | 最快时间 | 最慢时间 | 辅助存储 | 稳定性 |
---|---|---|---|---|---|
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 稳定 |
public static void selectSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
int index = i;
for (int j = i + 1; j < array.length; j++) {
//若是从大到小排,则此处改为<
if (array[j] < array[index]) {
index = j;
}
}
if (index != i) {
int temp = array[index];
array[index] = array[i];
array[i] = temp;
}
}
}
同冒泡排序一样,适用数据规模非常小的情况。
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
相当于从最后一个父结点开始跟子结点比较,和最大的那个子结点换。这个父结点搞定之后,就搞下一个父结点,直到把根节点搞定,就成为了大根堆。
排序方法 | 平均时间 | 最快时间 | 最慢时间 | 辅助存储 | 稳定性 |
---|---|---|---|---|---|
堆排序 | O(nlog2 n) | O(nlog2 n) | O(nlog2 n) | O(1) | 不稳定 |
public static void heapSort(int array[],int len){
//建堆 len/2-1 是最后一个非叶子结点
//只有 len/2 个结点才有子结点,所以先就只需要初始化0~len/2-1位,即len/2个
for(int i=len/2-1;i>=0;i--){
maxHeapify(array,i,len-1);
}
//排序,根节点和最后一个节点交换
//换完以后,取走根,重新建堆
//len-1 最后一个节点
for(int i=len-1;i>0;i--){
int temp=array[0];
array[0]=array[i];
array[i]=temp;
maxHeapify(array,0,i-1);
}
}
/**
* 调整堆
*/
private static void maxHeapify(int array[],int start,int end){
//父亲的位置
int dad=start;
//儿子的位置
int son=dad*2+1;
while(son<=end){//如果子节点下标在可以调整的范围内就一直调整下去
//如果没有右孩子就不用比,有的话,比较两个儿子,选择最大的出来
if(son+1 <= end && array[son]<array[son+1]){
son++;
}
//和父节点比大小
if(array[dad]>array[son]){
return;
}else{//父亲比儿子小,就要对整个子树进行调整
int temp=array[son];
array[son]=array[dad];
array[dad]=temp;
//递归下一层
dad=son;
son=dad*2+1;
}
}
}
双层嵌套循环,相邻两个元素如果不符合规则就两两换位,一直都是相邻两位互换。
排序方法 | 平均时间 | 最快时间 | 最慢时间 | 辅助存储 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n*n) | O(n) | O(n*n) | O(1) | 稳定 |
public static void bubbleSort(int[] array) {
//此方法为从前往后判断
for (int i = array.length - 1; i > 0; i--) {
boolean flag = false;
//若是从大到小排,则此处改为<
for (int j = 0; j < i; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
flag = true;
}
}
if(!flag){
return;
}
}
}
同简单选择排序一样,适用数据规模非常小的情况。
一直执行分区域执行快排算法,大至0-n,小至0-1的区域,都要执行快排。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BZCeLrmY-1591324997743)(C:\Users\Administrator\Downloads\未命名文件.jpg)]
排序方法 | 平均时间 | 最快时间 | 最慢时间 | 辅助存储 | 稳定性 |
---|---|---|---|---|---|
快速排序 | O(nlog2 n) | O(nlog2 n) | O(n^2) | O(logn) | 不稳定 |
//方法一:
public static void quickSort(int[] array, int begin, int end) {
if (end - begin <= 0) {
return;
}
int x = array[begin];
int low = begin;
int high = end;
//由于会从两头取数据,需要一个方向
boolean direction = true;
thisWhile:
while (low < high) {
if (direction) {//从右往左找
for (int i = high; i > low; i--) {
if (array[i] <= x) {
array[low++] = array[i];
high = i;
direction = false;
continue thisWhile;//跳转到thisWhile处,从thisWhile处开始执行
}
}
high = low;//如果上面的if从未进入,让两个指针重合
} else {
for (int i = low; i < high; i++) {
if (array[i] >= x) {
array[high--] = array[i];
low = i;
direction = true;
continue thisWhile;
}
}
low = high;
}
}
//把最后找到的值 放入中间位置
array[low] = x;//array[high]=x同样可以
//左右两侧进行快排
quickSort(array, begin, low - 1);
quickSort(array, low + 1, end);
}
//方法二:
public class kuaipai {
private static final String START = "startIndex";
private static final String END = "endIndex";
public static void main(String[] args) {
int[] arr = new int[]{4, 4, 6, 5, 3, 2, 8, 1};
// quickSort(arr, 0, arr.length - 1);
feiDiGuiQuickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
//方法二:
public static void quickSort(int[] arr, int startIndex, int endIndex) {
//递归结束条件:startIndex大于或等于endIndex时
if (startIndex >= endIndex) {
return;
}
//得到基准元素位置
int pivotIndex = partition(arr, startIndex, endIndex);
//根据基准元素,分成两部分进行递归排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
//方法三:
/**
* 快速排序法 递归 分治(双边循环法)
*
* @param arr
* @param startIndex
* @param endIndex
* @return
*/
private static int partition(int[] arr, int startIndex, int endIndex) {
//去第一个位置(也可以选择随机位置)的元素作为基准元素
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
while (left != right) {
//控制right指针比较,并左移
while (left < right && arr[right] > pivot) {
right--;
}
//交换left指针比较,并右移
while (left < right && arr[left] <= pivot) {
left++;
}
//交换left和right指针所指向的元素
if (left < right) {
int p = arr[left];
arr[left] = arr[right];
arr[right] = p;
}
}
//pivot和指针重合点交换
arr[startIndex] = arr[left];
arr[left] = pivot;
return left;
}
//方法四:
/**
* 快速排序法 递归 单边循环法
*
* @param arr
* @param startIndex
* @param endIndex
* @return
*/
private static int danPartition(int[] arr, int startIndex, int endIndex) {
//取第一个位置(也可以选择随机位置)的元素作为基准元素
int pivot = arr[startIndex];
int mark = startIndex;
for (int i = startIndex + 1; i <= endIndex; i++) {
if (arr[i] < pivot) {
mark++;
int p = arr[mark];
arr[mark] = arr[i];
arr[i] = p;
}
}
arr[startIndex] = arr[mark];
arr[mark] = pivot;
return mark;
}
//方法三:
/**
* 非递归实现快速排序
*
* @param arr
* @param startIndex
* @param endIndex
*/
private static void feiDiGuiQuickSort(int[] arr, int startIndex, int endIndex) {
Stack<Map<String, Integer>> stack = new Stack<>();
Map<String, Integer> map = new HashMap<>();
map.put(START, startIndex);
map.put(END, endIndex);
stack.push(map);
while (!stack.isEmpty()) {
Map<String, Integer> param = stack.pop();
int pivotIndex = feiDiGuiPartition(arr, param.get(START), param.get(END));
if (param.get(START) < pivotIndex - 1) {
Map<String, Integer> leftParam = new HashMap<>();
leftParam.put(START, param.get(START));
leftParam.put(END, pivotIndex - 1);
stack.push(leftParam);
}
if (pivotIndex + 1 < param.get(END)) {
Map<String, Integer> rightParam = new HashMap<>();
rightParam.put(START, pivotIndex + 1);
rightParam.put(END, param.get(END));
stack.push(rightParam);
}
}
}
private static int feiDiGuiPartition(int[] arr, int startIndex, int endIndex) {
int pivot = arr[startIndex];
int mark = startIndex;
for (int i = startIndex + 1; i <= endIndex; i++) {
if (arr[i] < pivot) {
mark++;
int p = arr[mark];
arr[mark] = arr[i];
arr[i] = p;
}
}
arr[startIndex] = arr[mark];
arr[mark] = pivot;
return mark;
}
}
快速排序适用于数据量大重复数据少数据是顺序存储结构的情况,不适用与链式存储结构。
排序方法 | 平均时间 | 最快时间 | 最慢时间 | 辅助存储 | 稳定性 |
---|---|---|---|---|---|
归并排序 | O(nlog2 n) | O(nlog2 n) | O(nlog2 n) | O(1) | 稳定 |
public static void mergeSort(int array[],int left,int right){
if(left==right){
return;
}else{
int mid=(left+right)/2;
//拆分过程
mergeSort(array,left,mid);
mergeSort(array,mid+1,right);
//合并过程
merge(array,left,mid+1,right);
}
}
private static void merge(int[] array,int left,int mid,int right){
int leftSize=mid-left;
int rightSize=right-mid+1;
//生成数组
int[] leftArray=new int[leftSize];
int[] rightArray=new int[rightSize];
//填充数据
for(int i=left;i<mid;i++){
leftArray[i-left]=array[i];
}
for(int i=mid;i<=right;i++){
rightArray[i-mid]=array[i];
}
//合并
int i=0;
int j=0;
int k=left;
//合并数组使其有序
while(i<leftSize && j<rightSize){
if(leftArray[i]<rightArray[j]){
array[k]=leftArray[i];
k++;i++;
}else{
array[k]=rightArray[j];
k++;j++;
}
}
//填充上面过程未被合并的余下数据
while(i<leftSize){
array[k]=leftArray[i];
k++;i++;
}
while(j<rightSize){
array[k]=rightArray[j];
k++;j++;
}
}
定义一个辅助数组: 定义一个一个二维数组来作为辅助存储空间。数组定义容量为a[10] [n],其中的n尽量大一点。
第一步: 根据个位数把数据存入新定义的数组:把11放入a[1] []中,把100放入a[0] []中,1009放入a[9] []中。
第二步: 把全部的数从数组a中依次放入原数组中,此时个位数已排序完成。
第三步: 根据十位数把数据存入新定义的数据:把21放入a[2] []中,把100放入a[0] []中,1891放入a[9] []中。
第四步: 把全部的数从数组a中依次放入原数组,此时十位数已排序完成。
…
最后一步: 如果全部数的第n位数都为0,且当前已经轮到第n位数,则当前的原数组就是排序完成后的数组,其个位数、十位数、百位数…最后一位数均为排序完成。
下图来自于菜鸟教程
排序方法 | 平均时间 | 最快时间 | 最慢时间 | 辅助存储 | 稳定性 |
---|---|---|---|---|---|
基数排序 | O(d(r+n)) | O(d(n+rd)) | O(d(r+n)) | O(rd+n) | 稳定 |
#include
#include
#include "string.h"
#include
#include "malloc.h"
struct AL//定义一个结构体用作辅助存储的存储类型(主要是需要thisIndex字段)
{
int thisIndex;
int a[100];
};
void RadixSort(int array[],int n){
struct AL ALs[10];
int i,j,k;
for(i=0;i<10;i++){
ALs[i].thisIndex = 0;
}
printf("\n");
int p = 1;
int index = 1;
while(p){
p = 0;//如果当前计算到千位,如果有一个值大于一千(有第四位)则pn=1,继续计算,如果判断为0则终止循环。
//根据数字的一位,存入辅助存储数组
for(i=0;i<n;i++){
int mi = 1;
for(j=0;j<index;j++){
mi *= 10;
}
if(array[i]/(mi/10)>0){
p++;
}
int q = array[i]%mi/(mi/10);
int in = ALs[q].thisIndex;
ALs[q].a[in] = array[i];
ALs[q].thisIndex++;
}
k = 0;
//从辅助存储数组中放入原数组
for(i=0;i<10;i++){
int end = ALs[i].thisIndex;
for(j=0;j<end;j++){
array[k] = ALs[i].a[j];
k++;
}
ALs[i].thisIndex = 0;
}
//排序的位加一,从个位变为十位、从十位变为百位
index++;
}
}
main(){
int array[10] = {10,123,456,54,21,814,23,15,56,5000};
int i;
for(i=0;i<10;i++){
printf("%d ",array[i]);
}
RadixSort(array,10);
for(i=0;i<10;i++){
printf("%d ",array[i]);
}
}
所有数的最大值和最小值所包含的区间比较小的情况。比如最大值为10,最小值为100,其中包含上千个数。
最难的情况是有一个数特别大,比其他的都要大个几千上万倍