排序算法基本原理及实现2

                                                                                             

                                   打牌 : da pai ge的个人主页
                                   ️个人专栏 : da pai ge的博客专栏
                                  ☁️宝剑锋从磨砺出,梅花香自苦寒来

️冒泡排序

原理

在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序

private void swap(int[] array, int i, int j) {

   int t = array[i];

   array[i] = array[j];

   array[j] = t;

}

private void createHeap(int[] array) {

   for (int i = (array.length - 1) / 2; i >= 0; i--) {

       shiftDown(array, array.length, i);

  }

}

public static void shiftDown(int[] array, int size, int index) {

   int left = 2 * index + 1;

   while (left < size) {

       int max = left;

  int right = 2 * index + 2;

       if (right < size) {

           if (array[right] > array[left]) {

               max = right;

          }

      }

       

       if (array[index] >= array[max]) {

           break;

      }

       

       int t = array[index];

       array[index] = array[max];

       array[max] = t;

       

       index = max;

       left = 2 * index + 1;

  }

}

时间复杂度

空间复杂度

最好O(n)

平均O(n^2)

最坏O(n^2)

数据有序数据逆序

实现

public static void bubbleSort(int[] array) {

for (int i = 0; i < array.length - 1; i++) {

boolean isSorted = true;

for (int j = 0; j < array.length - i - 1; j++) {

// 相等不交换,保证稳定性

if (array[j] > array[j + 1]) {

swap(array, j, j + 1);

isSorted = false;

}

}

if (isSorted) {

break;

}

}

}

快速排序

原理-总览

1. 从待排序区间选择一个数,作为基准值(pivot);

2. Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可

以包含相等的)放到基准值的右边;

3. 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间

的长度 == 0,代表没有数据。

实现:

public static void quickSort(int[] array) {

quickSortInternal(array, 0, array.length - 1);

}

// [left, right] 为待排序区间

private static void quickSortInternal(int[] array, int left, int right) {

if (left == right) {

return;

}

if (left > right) {

return;

}

// 最简单的选择基准值的方式,选择 array[left] 作为基准值

// pivotIndex 代表基准值最终停留的下标

int pivotIndex = partition(array, left, right);

// [left, pivotIndex - 1] 都是小于等于基准值的

// [pivotIndex + 1, right] 都是大于等于基准值的

quickSortInternal(array, left, pivotIndex - 1);

quickSortInternal(array, pivotIndex + 1, right);

}

原理-partition

Hoare :

实现:

private static int partition(int[] array, int left, int right) {

int i = left;

int j = right;

int pivot = array[left];

while (i < j) {

while (i < j && array[j] >= pivot) {

j--;

}



while (i < j && array[i] <= pivot) {

i++;

}



swap(array, i, j);

}

swap(array, i, left);

return i;

}

挖坑

基本思路和Hoare 法一致,只是不再进行交换,而是进行赋值(填坑+挖坑)

实现:

前后遍历法:

private static int partition(int[] array, int left, int right) {

int d = left + 1;

int pivot = array[left];

for (int i = left + 1; i <= right; i++) {

if (array[i] < pivot) {

swap(array, i, d);

d++;

}

}

swap(array, d, left);



return d;

}

性能分析

时间复杂度

空间复杂度

最好 平均O(n * log(n))

最坏 最好O(n * log(n))

平均 最坏O(n^2) O(log(n)) O(log(n)) O(n)

稳定性:不稳定

原理-基准值的选择

1. 选择边上(左或者右)

2. 随机选择

3. 几数取中(例如三数取中):array[left], array[mid], array[right] 大小是中间的为基准值

原理-非递归分治

public static void quickSort(int[] array) {

Stack stack = new Stack<>();

stack.push(array.length - 1);

stack.push(0);



while (!stack.isEmpty()) {

int left = stack.pop();

int right = stack.pop();

if (left >= right) {

continue;

}



int pivotIndex = partition(array, left, right);

stack.push(right);

stack.push(pivotIndex + 1);



stack.push(pivotIndex - 1);

stack.push(left);

}

}

优化总结

1. 选择基准值很重要,通常使用几数取中法

2. partition 过程中把和基准值相等的数也选择出来

3. 待排序区间小于一个阈值时(例如 48),使用直接插入排序

总结

1. 在待排序区间选择一个基准值

1. 选择左边或者右边

2. 随机选取

3. 几数取中法

2. 做 partition,使得小的数在左,大的数在右

1. hoare

2. 挖坑

3. 前后遍历

4. 将基准值相等的也选择出来(了解)

3. 分治处理左右两个小区间,直到小区间数目小于一个阈值,使用插入排序

归并排序

原理-总览

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and

Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子

序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

原理-合并两个有序数组

private static void merge(int[] array, int low, int mid, int high) {

int i = low;

int j = mid;

int length = high - low;

int[] extra = new int[length];

int k = 0;



// 选择小的放入 extra

while (i < mid && j < high) {

// 加入等于,保证稳定性

if (array[i] <= array[j]) {

extra[k++] = array[i++];

} else {

extra[k++] = array[j++];

}

}



// 将属于元素放入 extra

while (i < mid) {

extra[k++] = array[i++];

}



while (j < right) {

extra[k++] = array[j++];

}

时间复杂度

空间复杂度

O(n * log(n))

O(n)

数据不敏感

数据不敏感

// 从 extra 搬移回 array

for (int t = 0; t < length; t++) {

// 需要搬移回原位置,从 low 开始

array[low + t] = extra[t];

}

}

实现

public static void mergeSort(int[] array) {

mergeSortInternal(array, 0, array.length);

}

// 待排序区间为 [low, high)

private static void mergeSortInternal(int[] array, int low, int high) {

if (low - 1 >= high) {

return;

}



int mid = (low + high) / 2;

mergeSortInternal(array, low, mid);

mergeSortInternal(array, mid, high);



merge(array, low, mid, high);

}

你可能感兴趣的:(算法,java,数据结构)