印象中,三次面试都被问到了快速排序算法,有的甚至要求当场手写实现,就比如今天下午的转转面试。艰难困苦中,幸好我写出了一个不太正规的冒泡排序。这里就记录下常见的几种排序算法吧!
分类
- 基于“交换”的排序算法:冒泡排序、快速排序
- 基于“选择”的排序算法:简单选择排序(Simple Selection Sort)、树形选择排序(Tree Selection Sort)、堆排序(Heap Sort)
冒泡排序
/**
* Created by clearbug on 2018/2/27.
*/
public class Sort {
public static void main(String[] args) {
int[] arr = new int[]{49, 38, 65, 97, 76, 13, 27, 49};
Sort sort = new Sort();
sort.bubbleSort(arr);
}
/**
* 冒泡排序
*
* 原理:藉助“交换”操作
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 是否稳定:稳定
*
* @param arr 待排序的数组
*/
public void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
boolean sorted = true;
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
sorted = false;
}
}
if (sorted) {
break;
}
}
}
}
快速排序
/**
* Created by clearbug on 2018/2/27.
*/
public class Sort {
public static void main(String[] args) {
int[] arr = new int[]{49, 38, 65, 97, 76, 13, 27, 49};
Sort sort = new Sort();
sort.quickSort(arr);
}
/**
* 快速排序
*
* 原理:藉助“交换”操作,是冒泡排序的一种改进
* 时间复杂度:O(nlogn)
* 空间复杂度:O(1)。但是需要一个栈空间来实现递归,栈的最大深度为 log2n + 1,最坏情况下栈深度为 n。
* 是否稳定:不稳定
* 注意点:
* 1. 通常,快速排序被认为是在所有同数量级(O(nlogn))的排序方法中平均性能最好;
* 2. 快速排序算法中,若待排序数组是有序的,那么快速排序将蜕化为冒泡排序,其时间复杂度为 O(n^2);如何改善这一点?记录一次遍历中
* 是否有“交换”操作,若没有则不再进行递归排序了;
*
* @param arr 待排序的数组
*/
public void quickSort(int[] arr) {
qSort(arr, 0, arr.length - 1);
}
public void qSort(int[] arr, int low, int high) {
if (low < high) {
int pivotLoc = partition(arr, low, high);
qSort(arr, low, pivotLoc - 1);
qSort(arr, pivotLoc + 1, high);
}
}
public int partition(int[] arr, int low, int high) {
int pivotKey = arr[low];
while (low < high) {
while (low < high && arr[high] >= pivotKey) {
high--;
}
arr[low] = arr[high];
while (low < high && arr[low] <= pivotKey) {
low++;
}
arr[high] = arr[low];
}
arr[low] = pivotKey;
return low;
}
}
简单选择排序
import java.util.Arrays;
/**
* Created by clearbug on 2018/2/27.
*/
public class Sort {
public static void main(String[] args) {
int[] arr = new int[]{49, 38, 65, 97, 76, 13, 27, 49};
Sort sort = new Sort();
sort.simpleSelectionSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 简单选择排序
*
* 原理:藉助“选择”操作
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 是否稳定:不稳定
* 注意点:暂无
*
* @param arr 待排序的数组
*/
public void simpleSelectionSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int minIndex = selectMinKey(arr, i);
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
/**
* 寻找并返回数组 arr 中从索引 start 开始的最小值的索引位置
*
* @param arr 待排序的数组
* @param start 寻找最小值起始索引未知
* @return 最小值的索引未知
*/
private int selectMinKey(int[] arr, int start) {
int res = start;
for (int i = start + 1; i < arr.length; i++) {
if (arr[i] < arr[res]) {
res = i;
}
}
return res;
}
}
树形选择排序
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
/**
* Created by clearbug on 2018/2/27.
*/
public class Sort {
static class BinaryTree {
public TreeNode root;
}
static class TreeNode {
public int val;
public TreeNode leftChild;
public TreeNode rightChild;
public boolean from; // true 为 left,false 为 right
public TreeNode(int val) {
this.val = val;
}
}
public static void main(String[] args) {
int[] arr = new int[]{49, 38, 65, 97, 76, 13, 27, 49};
Sort sort = new Sort();
sort.treeSelectionSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 树形选择排序(Tree Selection Sort),又称锦标赛排序(Tournament Sort),是一种按照锦标赛的思想进行选择排序的方法,是对
* 简单选择排序算法的一种改进。
*
* 原理:藉助“选择”操作
* 时间复杂度:O(nlogn)
* 空间复杂度:O(1)
* 是否稳定:不稳定
* 注意点:暂无
*
* @param arr 待排序的数组
*/
public int[] treeSelectionSort(int[] arr) {
TreeNode[] treeNodes = new TreeNode[arr.length];
for (int i = 0; i < arr.length; i++) {
treeNodes[i] = new TreeNode(arr[i]);
}
int[] res = new int[arr.length];
Queue<TreeNode> queue = new LinkedList<>();
for (int i = 0; i < arr.length; i++) {
TreeNode node = new TreeNode(i);
queue.add(node);
}
return res;
}
private TreeNode buildTree(TreeNode[] nodes) {
if (nodes.length == 2) {
TreeNode root = new TreeNode(Integer.MAX_VALUE);
if (nodes[0].val > nodes[1].val) {
root.val = nodes[1].val;
// root.from =
}
}
int parentNodesLen = nodes.length / 2 + nodes.length % 2;
TreeNode[] parentNodes = new TreeNode[parentNodesLen];
for (int i = 0; i < nodes.length; i++) {
}
}
private int selectMin(Queue<TreeNode> queue) {
Queue<TreeNode> parentQueue = new LinkedList<>();
if (queue.size() == 2) {
TreeNode first = queue.poll();
TreeNode second = queue.poll();
return first.val > second.val ? second.val : first.val;
}
if (queue.size() % 2 == 1) {
queue.add(new TreeNode(Integer.MIN_VALUE));
}
while (!queue.isEmpty() && queue.size() % 2 == 0) {
TreeNode first = queue.poll();
TreeNode second = queue.poll();
if (first.val > second.val) {
parentQueue.add(second);
} else {
parentQueue.add(first);
}
}
if (queue.isEmpty()) {
return selectMin(parentQueue);
} else {
return queue.poll().val;
}
}
}
好吧,我承认我没写出来呢!网上相关实现也不多,就看到一哥们的 c++ 实现:http://blog.youkuaiyun.com/caroline_wendy/article/details/30038115。这种算法思路虽然很简单,但是代码实现起来确实有点复杂,以后有时间了再来写下实现吧!
堆排序
堆排序是在树形选择排序的基础上进行了优化,比树形选择排序稍微难理解一点,但是空间效率确实提高了的。真的要写其实现的话还是有点难度的,所以我抄写了这个老哥的代码:https://www.geeksforgeeks.org/heap-sort/:
import java.util.Arrays;
/**
* Created by clearbug on 2018/2/27.
*/
public class Sort {
public static void main(String[] args) {
int[] arr = new int[]{49, 38, 65, 97, 76, 13, 27, 49};
Sort sort = new Sort();
sort.heapSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 堆排序(Heap Sort),是对树形选择排序的一种改进
*
* 原理:藉助“选择”操作
* 时间复杂度:O(nlogn)
* 空间复杂度:O(1)
* 是否稳定:不稳定
* 注意点:暂无
*
* @param arr 待排序的数组
*/
public void heapSort(int[] arr) {
int n = arr.length;
// Build heap (rearrange array)
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// One by one extract an element from heap
for (int i = n - 1; i >= 0; i--) {
// Move current root to end
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
// To heapify a subtree rooted with node i which is an index in arr[]. n is size of heap
private void heapify(int[] arr, int n, int i) {
int largest = i; // Initialize largest as root
int left = 2 * i + 1; // left = 2*i + 1
int right = 2 * i + 2; // right = 2*i + 2
// If left child is larger than root
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
// If right child is larger than largest so far
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
// If largest is not root
if (largest != i) {
int temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
// Recursively heapify the affected sub-tree
heapify(arr, n, largest);
}
}
}
归并排序
下面实现描述的只是2-路归并排序,还有其他形式的归并排序,但是我手头目前没有介绍其他归并排序的书籍,所以以后遇到用到再说!
import java.util.Arrays;
/**
* Created by clearbug on 2018/2/27.
*/
public class Sort {
public static void main(String[] args) {
int[] arr = new int[]{49, 38, 65, 97, 76, 13, 27, 49};
Sort sort = new Sort();
sort.mergeSort2(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 归并排序算法二(迭代法)
*
* 时间复杂度:O(nlogn)
* 空间复杂度:O(n)
*
* @param arr
*/
public void mergeSort2(int[] arr) {
int len = arr.length;
int[] result = new int[len];
int block, start;
for (block = 1; block < len; block *= 2) {
for (start = 0; start < len; start += 2 * block) {
int low = start;
int mid = (start + block) < len ? (start + block) : len;
int high = (start + 2 * block) < len ? (start + 2 * block) : len;
int start1 = low, end1 = mid;
int start2 = mid, end2 = high;
while (start1 < end1 && start2 < end2) {
result[low++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
}
while (start1 < end1) {
result[low++] = arr[start1++];
}
while (start2 < end2) {
result[low++] = arr[start2++];
}
}
int[] temp = arr;
arr = result;
result = temp;
}
// Java 是传值调用,而 arr 是引用,那么最终的 arr 可能跟入参的 arr 指向同一个地址
for (int i = 0; i < len; i++) {
result[i] = arr[i];
}
}
/**
* 归并排序算法一(递归法)
*
* 时间复杂度:O(nlogn)
* 空间复杂度:O(n)
*
* @param arr
*/
public void mergeSort(int[] arr) {
int len = arr.length;
int[] result = new int[len];
mergeSortRecursive(arr, result, 0, len - 1);
}
private void mergeSortRecursive(int[] arr, int[] result, int start, int end) {
if (start >= end) {
return;
}
int len = end - start, mid = (len >> 1) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
mergeSortRecursive(arr, result, start1, end1);
mergeSortRecursive(arr, result, start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2) {
result[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
}
while (start1 <= end1) {
result[k++] = arr[start1++];
}
while (start2 <= end2) {
result[k++] = arr[start2++];
}
for (int i = start; i <= end; i++) {
arr[i] = result[i];
}
}
}