Classical sorting algorithm summarized

Algorithm Overview

Classification algorithms

10 common sorting algorithm can be divided into two categories:

  • Comparison class sorting : to determine the relative order between the elements by comparing, since it can not break through time complexity O (nlogn), it is also known to sort non-linear time-based comparison.
  • Comparison of non-sorted categories : not determined by the relative order between the comparison element, which can break through the lower bound based on the comparison time ordered, run in linear time, also known as non-linear time-based ordering comparison.

hhh

Algorithm complexity

image.png

Related concepts

  • Stable : if a a b still in front after the original in front of b, and a = b, sort.
  • Unstable : if a then b originally in front, and a = b, a sort may occur in the back of b.
  • Time complexity : the total number of data sorting operation. When n changes reflect what law the number of operations presented.
  • Space complexity : the desired metric is a function of the storage space when the algorithm is executed in a computer, it is also the data size of n.

Bubble Sort (Bubble Sort)

Moving map presentation

hh

Java implementation

/**
 * 冒泡排序 平均时间复杂度为o(n2),最好复杂度为o(n),空间复杂度为o(1)
 * 
 * @author monkJay
 */
public class BubbleSort {

	public static void solution(int[] array) {
		int len = array.length;
		// 从第一个开始直到倒数第二个
		for (int i = 0; i < len - 1; ++i) {
			// 每比较一轮,都会有一个已排序好的数字,i就是已经排序好了的个数
			for (int j = 1; j < len - i; ++j) {
				// 如果后面的数字小于前面的数字,则交换
				if (array[j] < array[j - 1]) {
					int tmp = array[j];
					array[j] = array[j - 1];
					array[j - 1] = tmp;
				}
			}
		}
	}

	public static void main(String[] args) {
		int[] array = new int[] { 2, 1, 4, 3, 6, 5, 9, 8 };
		solution(array);
		for (int i = 0; i < array.length; ++i) {
			System.out.print(array[i] + " ");
		}
	}
}
复制代码

Select the sort (Selection Sort)

Moving map presentation

hsss

Java implementation

/**
 * 选择排序(最稳定的排序算法,不算什么序列时间复杂度都是o(n2)) 每次从未排序的序列中选择一个最小(大)的数放在已排序的序列的后面
 * 平均时间复杂度为o(n2),最好复杂度为o(n2),空间复杂度为o(1)
 * 
 * @author monkJay
 */
public class SelectionSort {

	public static void solution(int[] array) {
		int len = array.length;
		for (int i = 0; i < len - 1; ++i) {
			int minIndex = i;
			for (int j = i + 1; j < len; ++j) {
				if (array[minIndex] > array[j]) {
					minIndex = j;
				}
			}
			int tmp = array[minIndex];
			array[minIndex] = array[i];
			array[i] = tmp;
		}
	}
}
复制代码

Insertion sort (Insertion Sort)

Moving map presentation

you dddas

Java implementation

/**
 * 插入排序 | 选择未排序序列的第一个数,插入到已排序序列中合适得位置(从后往前找,直到找到一个比当前数小或者等于的数,插在它后面)
 * 平均时间复杂度为o(n2),最好复杂度为o(n),空间复杂度为o(1)
 * 
 * @author monkJay
 */
public class InsertionSort {

	public static void solution(int[] array) {
		int len = array.length;
		for (int i = 1; i < len; ++i) {
			int preIndex = i - 1;
			int current = array[i];
			while (preIndex >= 0 && array[preIndex] > current) {
				// 如果有序序列中的数大于要插入的数,则后移一个位置(将自己的位置空出来)
				array[preIndex + 1] = array[preIndex];
				// 继续往前寻找
				--preIndex;
			}
			// 找到了适合的位置(终于找到一个小于等于要插入的数了),将数字插在这个位置后面
			array[preIndex + 1] = current;
		}
	}
}
复制代码

Hill sorting (Shell Sort)

Moving map presentation

sss

Java implementation

/**
 * 希尔排序 将一个序列在逻辑上根据指定的步长分组,对每个分组进行插入排序(插入排序时的移动是根据当前步长移动查找的)
 * 
 * @author monkJay
 */
public class ShellSort {

	public static void solution(int[] array) {
		int len = array.length;
		// 步长从当前长度的一半开始,每一轮将步长减为一半,直到最后小于1结束
		for (int gap = len / 2; gap > 0; gap /= 2) {
			// 取值从步长开始,这里其实就是将直接插入排序中的1全部换成了步长gap
			// 其它和直接插入排序一样
			for (int i = gap; i < len; ++i) {
				int preIndex = i - gap;
				int current = array[i];
				while (preIndex >= 0 && array[preIndex] > current) {
					array[preIndex + gap] = array[preIndex];
					preIndex = preIndex - gap;
				}
				array[preIndex + gap] = current;
			}
		}
	}
}
复制代码

Merge sort (Merge Sort)

Moving map presentation

ddsds

Java implementation

/**
 * 二路归并排序 用到了分治的思想,不断往下细分,分到只有一个数字(认为是有序的)的时候开始合并,最后合并为一个有序的数组
 * 
 * @author monkJay
 */
public class MergeSort {

	public static void solution(int[] array) {
		int len = array.length;
		if (len == 0) {
			return;
		}
		int left = 0;
		int right = len - 1;
		int[] tmp = new int[len];
		mergeSort(array, tmp, left, right);
	}

	public static void mergeSort(int[] array, int[] tmp, int left, int right) {
		// 如果还可以分组,其实最后是分到只有一个数(认为一个数是有序的)
		if (left < right) {
			int center = left + (right - left) / 2;
			// 左边的数组继续往下分
			mergeSort(array, tmp, left, center);
			// 右边的数组继续往下分
			mergeSort(array, tmp, center + 1, right);
			// 将左右两个有序的数组合并
			merge(array, tmp, left, center, right);
		}
	}

	public static void merge(int[] array, int[] tmp, int left, int center,
			int right) {
		int i = left, j = center + 1;
		// 将要合并的两个有序数组,从小到大有序的放在辅助数组中
		for (int k = left; k <= right; ++k) {
			// 如果左边的数组遍历完了,就直接放右边的数组
			if (i > center) {
				tmp[k] = array[j++];
			}
			// 如果右边的数组遍历完了,就直接放左边的数组
			else if (j > right) {
				tmp[k] = array[i++];
			}
			// 将小的放在辅助数组中
			else if (array[i] <= array[j]) {
				tmp[k] = array[i++];
			} else {
				tmp[k] = array[j++];
			}
		}
		// 将辅助数组中的值复制到原数组中
		for (int k = left; k <= right; ++k) {
			array[k] = tmp[k];
		}
	}
}
复制代码

Quick Sort (Quick Sort)

Moving map presentation

ss

Java implementation

/**
 * 快速排序
 * 通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,
 * 则可分别对这两部分记录继续进行排序,以达到整个序列有序。
 * @author monkJay
 */
public class QuickSort {

	public static void solution(int[] array) {
		int len = array.length;
		if (len == 0) {
			return;
		}
		int left = 0, right = len - 1;
		quickSort(array, left, right);
	}

	public static void quickSort(int[] array, int left, int right) {
		if (left < right) {
			int partitionIndex = partition(array, left, right);
			quickSort(array, left, partitionIndex - 1);
			quickSort(array, partitionIndex + 1, right);
		}
	}

    // 第一种分区方法
	public static int partition(int[] array, int left, int right) {
        // 选取左边第一个数作为中轴数
		int pivot = left;
        // 从中轴数后面一个位置开始比较
		int index = pivot + 1;
        // 保证指针左边的数都是小于中轴数的
		for (int i = left + 1; i <= right; i++) {
            // 如果小于中轴数,将当前数的位置和中轴指针交换
			if (array[i] < array[pivot]) {
				swap(array, i, index);
                // 指针继续后移
				++index;
			}
		}
        // 将中轴数的位置和指针左边一个位置交换,也就是把中轴数换到中间
		swap(array, pivot, index - 1);
        // 最后返回这个中轴数的位置
		return index - 1;
	}
    // 第二种分区方法
	// public static int partition(int[] array, int left, int right) {
	// int i = left, j = right + 1;
	// int pivot = array[left];
	// while (i < j) {
	//
	// while (array[--j] > pivot && i < j)
	// ;
	// while (array[++i] < pivot && i < j)
	// ;
	// if (i < j) {
	// swap(array, i, j);
	// }
	// }
	// swap(array, left, j);
	// return j;
	// }

	public static void swap(int[] array, int a, int b) {
		int tmp = array[a];
		array[a] = array[b];
		array[b] = tmp;
	}
}
复制代码

Sort heap (Heap Sort)

Moving map presentation

sds

Java implementation

/**
 * 堆排序 先构建一个最大(小)堆,每次都堆顶取一个数,并把最后一个数交换到堆顶,然后重新调整堆,重复,直到堆中最后一个数
 * 最大堆得到从小到大的排序,最小堆得到从大到小的排序
 * 
 * @author HP
 *
 */
public class HeapSort {

	public static void solution(int[] array) {
		int len = array.length;
		if (len == 0) {
			return;
		}
		buildHeap(array);
		heapSort(array);
	}

	/**
	 * 下沉调整 用于堆的删除,还可用于构建二叉堆
	 * 
	 * @param array 存放堆的数组
	 * @param parentIndex 父结点位置
	 * @param len 数组长度
	 */
	public static void downAdjust(int[] array, int parentIndex, int len) {
		// 先计算得到左孩子
		int childIndex = parentIndex * 2 + 1;
		// 保存父结点的值
		int tmp = array[parentIndex];
		while (childIndex < len) {
			// 如果右孩子存在,且右孩子的值比左孩子要小,则将孩子节点定位到右孩子
			if (childIndex + 1 < len
					&& array[childIndex + 1] < array[childIndex]) {
				childIndex++;
			}
			// 如果父结点的值比两个孩子都小,不用调整了,直接结束
			if (tmp < array[childIndex]) {
				break;
			}
			// 将孩子节点的值赋给父结点,继续下沉
			array[parentIndex] = array[childIndex];
			// 将孩子节点作为新的父结点
			parentIndex = childIndex;
			// 重新计算左孩子的位置
			childIndex = parentIndex * 2 + 1;
		}
		// 节点调整结束,将最初需要调整的那个节点赋值到最终位置
		array[parentIndex] = tmp;
	}

	/**
	 * 其实就是让每个非叶子节点依次下沉
	 * 构造堆 时间复杂度是线性的
	 * @param array 要构造堆的数组
	 */
	public static void buildHeap(int[] array) {
		int len = array.length;
		// 找到最后一个节点的父结点
		int startIndex = (len - 2) / 2;
		for (int i = startIndex; i >= 0; --i) {
			downAdjust(array, i, len);
		}
	}

	/**
	 * 堆排序 每次都堆顶取一个数,并把最后一个数交换到堆顶,然后重新调整堆,重复,直到堆中最后一个数
	 * @param array 
	 */
	public static void heapSort(int[] array) {
		for (int i = array.length - 1; i > 0; --i) {
			int tmp = array[0];
			array[0] = array[i];
			array[i] = tmp;
			// 从堆顶开始调整
			downAdjust(array, 0, i);
		}
	}
}
复制代码

Counting sequencing (Counting Sort)

Moving map presentation

sss

Java implementation

/**
 * 计数排序
 * 计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。
 * 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
 * @author HP
 */
public class CountingSort {

	public static void solution(int[] array) {
		int len = array.length;
		if (len == 0) {
			return;
		}
		int maxValue = 0;
        // 得到最大值
		for (int i = 0; i < len; ++i) {
			if (array[i] > maxValue) {
				maxValue = array[i];
			}
		}
		int[] bucket = new int[maxValue + 1];
		// 将桶用0填充
		Arrays.fill(bucket, 0);
		// 记录每个元素出现的个数
		for (int i = 0; i < len; ++i) {
			bucket[array[i]]++;
		}
		int index = 0;
		for (int i = 0; i < bucket.length; ++i) {
            // 将桶中的元素往原数组放
			while (bucket[i] > 0) {
				array[index++] = i;
				bucket[i]--;
			}
		}
	}
}
复制代码

topK four solution

Construction heap big top

Algorithm Analysis: k number of array configured with a front top large stack, and then sequentially comparing the number of the latter, is less than the top of the stack, is replaced, readjusted stack, the final element is the top of the stack of the k-th largest element. The time complexity is O (klogk). Java implementation

public class TopK {

	public static int topK_Solution(int[] input, int k) {
		int len = input.length;
		if (len == 0 || k <= 0 || k > len) {
			return 0;
		}
        // 先把前k个数构造成一个堆
		int[] array = Arrays.copyOfRange(input, 0, k);
		buildHeap(array);
        // 遍历后面的数,如果小于堆顶元素,就把堆顶换掉,并重新下沉调整堆
		for (int i = k; i < len; ++i) {
			if (input[i] < array[0]) {
				array[0] = input[i];
				downAdjust(array, 0, array.length);
			}
		}
		return array[0];
	}

    // 构造堆
	public static void buildHeap(int[] array) {
		int startIndex = (array.length - 2) / 2;
		for (int i = startIndex; i >= 0; --i) {
			downAdjust(array, i, array.length);
		}
	}

	// 调整最大堆
	public static void downAdjust(int[] array, int parentIndex, int len) {
		int tmp = array[parentIndex];
		int childIndex = parentIndex * 2 + 1;
		while (childIndex < len) {
			if (childIndex + 1 < len
					&& array[childIndex + 1] > array[childIndex]) {
				childIndex++;
			}
			if (tmp > array[childIndex]) {
				break;
			}
			array[parentIndex] = array[childIndex];
			parentIndex = childIndex;
			childIndex = parentIndex * 2 + 1;
		}
		array[parentIndex] = tmp;
	}

	public static void main(String[] args) {
		int[] array = new int[] { 2, 1, 4, 3, 6, 5, 7, 9, 8 };
		int res = GetLeastNumbers_Solution(array, 5);
		System.out.print("第K大的元素是:" + res);
	}
}
复制代码

Structuring the top of the heap

Algorithm Analysis: First the array is configured top small heap, then the heap k - 1 times Delete top of the heap of operation, the last top of the heap k-th largest element is required. The time complexity is O (klogN).

public class Solution {
    public int topK_Solution(int [] input, int k) {
        int len = input.length;
        if (len == 0 || k <= 0 || k > len){
            return 0;
        }
        // 先构造一个堆
        bulidHeap(input);
        // 将数组进行k - 1次删除堆顶操作
        for (int i = len - 1; i > len - k; --i){
            int tmp = input[0];
            input[0] = input[i];
            input[i] = tmp;
            downAdjust(input, 0, i);
        }
        // 返回最后的堆顶
        return input[0];
    }
    public void bulidHeap(int[] array){
        int startIndex = (array.length - 2) / 2;
        for (int i = startIndex; i >= 0; --i){
            downAdjust(array, i, array.length);
        }
    }
     public void downAdjust(int[] array, int parentIndex, int len){
            int tmp = array[parentIndex];
            int childIndex = parentIndex * 2 + 1;
            while (childIndex < len){
                if (childIndex + 1 < len && array[childIndex + 1] < array[childIndex]){
                    childIndex ++;
                }
                if (tmp < array[childIndex]){
                    break;
                }
                array[parentIndex] = array[childIndex];
                parentIndex = childIndex;
                childIndex = parentIndex * 2 + 1;
            }
            array[parentIndex] = tmp;
        }
}
复制代码

Priority queue using Java

Algorithm Analysis: In fact, with the front of a stack is the same, just in a Java heap has been packaged, the code is more concise (but seemingly time-consuming to be here a little longer, in fact, here to remove the top of the heap without adjustment, but every time it here are adjusted, so a waste of time)

public class Solution {
    public int topK_Solution(int [] input, int k) {
        int len = input.length;
        if (len == 0 || k <= 0 || k > len){
            return 0;
        }
        // 使用优先队列构造一个最大堆,这里用了lambda表达式
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, (o1, o2) -> {
            return o2.compareTo(o1);
        });
        for (int i = 0; i < len; ++i) {
            // 往堆中添加数字,直到堆的大小为k
            if (maxHeap.size() != k) {
                maxHeap.offer(input[i]);
            }
            // 如果当前元素比堆顶的元素要小,那么将堆顶出队列,将当前元素加入队列(内部会自动调整堆)
            else if (input[i] < maxHeap.peek()) {
                maxHeap.poll();
                maxHeap.offer(input[i]);
            }
        }
        // 返回堆顶元素
        return maxHeap.peek();
    }
}
复制代码

The idea of ​​using the fast row

Algorithm Analysis: k is adjusted based on the number of the array, so that the last number is less than k, k are the left, the number k is greater than k in the right side, then k is the k-th largest element

public class Solution {
    public int topK_Solution(int [] input, int k) {
        int len = input.length;
        if (len == 0 || k <= 0 || k > len){
            return 0;
        }
        int start = 0;
        int end = len - 1;
        int index = partition(input, start, end);
        while (index != k - 1){
            if (index > k - 1){
                end = index - 1;
                index = partition(input, start, end);
            } else {
                start = index + 1;
                index = partition(input, start, end);
            }
        }
        return input[k - 1];
    }
    public int partition(int[] input, int left, int right){
        int pivot = left;
        int index = pivot + 1;
        for (int i = left; i <= right; ++i){
            if (input[i] < input[pivot]){
                swap(input, i, index);
                ++index;
            }
        }
        swap(input, pivot, index - 1);
        return index - 1;
    }
    public void swap(int[] input, int a, int b){
        int tmp = input[a];
        input[a] = input[b];
        input[b] = tmp;
    }
}
复制代码

Guess you like

Origin juejin.im/post/5df84bdf518825125a63896f