/**本文假设数据用数组储存,排序元素为整数,升序排序**/
目录
一、选择排序
二、插入排序
三、冒泡排序
四、归并排序
五、快速排序
六、堆排序
七、桶排序
八、基数排序
九、外部排序
一、选择排序
思路:在数组中找到最小元素,并将其和第一个元素交换,直到数组中仅剩最后一个元素。
先假设第一个数为最小,则最小元素的下标为0,然后与后面的数挨个进行比较,当后面的数比当前最小数小时,更新最小下标。由此找到最小数,将它和第一个数交换。此时第一个数已经排好了。
然后再设第二个数最小,重复上述过程,前两个数就排好了。
循环直到数组中只剩最后一个数,则全部排序完毕。
时间复杂度O(n^2)
public static void selectionSort(int[] nums) {
int len = nums.length;
for(int i = 0; i < len - 1; i++) {
int min = nums[i];
int minI = i;
for(int j = i + 1; j < len; j++) {
if(min > nums[j]) {
minI = j;
min = nums[j];
}
}
int temp = nums[i];
nums[i] = min;
nums[minI] = temp;
}
}
二、插入排序
思路:从第一位开始重复将新的元素添加到一个已经排好序的数组中。
从 i 为1开始,
假设前 i 个数都已经排好,现在插入第 i + 1 个数,先将第 i + 1 个数放到临时变量currentE中,与前面的数比较,若小于前面的数,则让前面的数向后移动一位,直到currentE大于前面的数,则把currentE中的数放在这个数的后面。这样前 i + 1 个数就排好了。
时间复杂度O(n^2)
public static void insertionSort(int[] nums) {
for(int i = 0; i < nums.length; i++) {
int k;
int currentE = nums[i];
for(k = i - 1; k >= 0 && currentE < nums[k]; k--) {
nums[k + 1] = nums[k];
}
nums[k + 1] = currentE;
}
}
三、冒泡排序
思路:多次遍历数组,每次遍历中连续比较相邻元素,如果元素没有按顺序排列,则互换位置,否则保持不变。
第一次遍历后,最后一个元素最大,第二次遍历后倒数第二个元素成为第二大,最多遍历n - 1 次排完所有数。若在某次遍历中没有元素互换位置,则不必继续遍历,因为所有元素都已经排好了。
时间复杂度O(n^2)
public static void bubbleSort(int[] nums) {
boolean next = true;
for(int i = 0; i < nums.length && next; i++) {
next = false;
for(int j = 0; j < nums.length - 1 - i; j++) {
if(nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
next = true;
}
}
}
}
四、归并排序
思路:将数组分为两半,递归使用归并排序后合并。
当要排序的数组长度大于一时,先创建两个新数组,分别把数组的左右两部分复制到新数组中,对新数组递归使用归并排序,然后把左右两部分数组合并起来。
public static void mergeSort(int[] nums) {
if(nums.length > 1) {
int leftLen = nums.length / 2;
int[] left = new int[leftLen];
System.arraycopy(nums, 0, left, 0, leftLen);
mergeSort(left);
int rightLen = nums.length - leftLen;
int[] right = new int[rightLen];
System.arraycopy(nums, leftLen, right, 0, rightLen);
mergeSort(right);
merge(left, right, nums);
}
}
private static void merge(int[] left, int[] right, int[] temp) {
int leftIndex = 0;
int rightIndex = 0;
int tempIndex = 0;
while(leftIndex < left.length && rightIndex < right.length) {
if(left[leftIndex] < right[rightIndex]) {
temp[tempIndex++] = left[leftIndex++];
}
else {
temp[tempIndex++] = right[rightIndex++];
}
}
while(leftIndex < left.length) {
temp[tempIndex++] = left[leftIndex++];
}
while(rightIndex < right.length) {
temp[tempIndex++] = right[rightIndex++];
}
}
时间复杂度O(n*logn) ,空间复杂度O(n)
上述方法由于频繁开辟新空间,使得空间复杂度较高,下面给出一个改进方法,通过保存左右指针代替创建新数组。
public static void mergeSort(int[] nums) {
//递归外开辟空间防止频繁开辟
int[] temp = new int[nums.length];
mergeSort(nums, 0, nums.length - 1, temp);
}
private static void mergeSort(int[] nums, int leftIndex, int rightIndex, int[] temp) {
if(leftIndex < rightIndex) {
int midIndex = (leftIndex + rightIndex) / 2;
mergeSort(nums, leftIndex, midIndex, temp);
mergeSort(nums, midIndex + 1, rightIndex, temp);
merge(nums, leftIndex, midIndex, rightIndex, temp);
}
}
private static void merge(int[] nums, int left, int mid, int right, int[] temp) {
int tempIndex = 0;
int leftIndex = left;
int rightIndex = mid + 1;
while(leftIndex <= mid && rightIndex <= right) {
if(nums[leftIndex] < nums[rightIndex]) {
temp[tempIndex++] = nums[leftIndex++];
}
else {
temp[tempIndex++] = nums[rightIndex++];
}
}
while(leftIndex <= mid) {
temp[tempIndex++] = nums[leftIndex++];
}
while(rightIndex <= right) {
temp[tempIndex++] = nums[rightIndex++];
}
tempIndex = 0;
while(left <= right) {
nums[left++] = temp[tempIndex++];
}
}
五、快速排序
思路:先在数组中寻找一个基,默认第一个元素,然后保存两个指针high和low。
由于要升序,所以high指针从最后一个元素开始遍历,当high指针大于基时向前移动,小于基时停止,此时high代表的元素小于基。low指针从第一个元素开始遍历,当low指针小于基时向后移动,大于基时停止,此时low代表元素大于基。这时low与high中元素互换,则满足要求继续上述遍历。
当low与high指针相遇时,遍历结束。此时由于是high指针先移动,所以此时指针所对应值小于基,基与指针值交换,排序结束。
public static void quickSort(int[] nums) {
quickSort(nums, 0, nums.length - 1);
}
private static void quickSort(int[] nums, int start, int end) {
if(end > start) {
int base = partition(nums, start, end);
quickSort(nums, start, base - 1);
quickSort(nums, base + 1, end);
}
}
//将base定位到正确位置,左侧小于base,右侧大于base
private static int partition(int[] nums, int start, int end) {
int base = nums[start];
int low = start;
int high = end;
while(low < high) {
while(low < high && nums[high] >= base) {
high--;
}
while(low < high && nums[low] <= base) {
low++;
}
if(low < high) {
int temp = nums[high];
nums[high] = nums[low];
nums[low] = temp;
}
}
nums[start] = nums[high];
nums[high] = base;
return high;
}
平均时间复杂度O(n*logn),最差O(n^2) ,空间复杂度O(1)
六、堆排序
首先先构造一个堆类,它是一个完全二叉树,其中根节点大于它的子节点。
添加元素时添加到列表末尾,若比父节点大则与父节点交换位置,比父节点小则添加完毕。
删除元素时,把尾节点元素代替根节点,并删除尾节点。若此时根节点元素小于子节点,交换位置,直到根节点大于字节点。
public class Heap<E extends Comparable<? super E>> {
private List<E> list = new ArrayList<>();
public Heap() {
}
public Heap(E[] theList) {
for(int i = 0; i < theList.length; i++) {
add(theList[i]);
}
}
public void add(E t) {
list.add(t);
int currentIndex = list.size() - 1;
//判断是否为根节点
while(currentIndex > 0) {
int parentIndex = (currentIndex - 1) / 2;
if(list.get(currentIndex).compareTo(list.get(parentIndex)) > 0) {
swap(currentIndex, parentIndex);
currentIndex = parentIndex;
}
else {
break;
}
}
}
public E remove() {
if(list.size() == 0) {
return null;
}
E removed = list.get(0);
list.set(0, list.get(list.size() - 1));
list.remove(list.size() - 1);
int currentIndex = 0;
while(currentIndex < list.size()) {
int leftIndex = 2 * currentIndex + 1;
int rightIndex = 2 * currentIndex + 2;
//左节点越界直接退出
if(leftIndex >= list.size()) {
break;
}
int maxIndex = leftIndex;
//右节点没越界开始讨论,越界最大即左节点
if(rightIndex < list.size()) {
if(list.get(leftIndex).compareTo(list.get(rightIndex)) < 0) {
maxIndex = rightIndex;
}
}
if(list.get(currentIndex).compareTo(list.get(maxIndex)) < 0) {
swap(maxIndex, currentIndex);
currentIndex = maxIndex;
}
else {
break;
}
}
return removed;
}
private void swap(int currentIndex, int parentIndex) {
E temp = list.get(parentIndex);
list.set(parentIndex, list.get(currentIndex));
list.set(currentIndex, temp);
}
}
要排序数组,只需新建一个堆,把数组中数加入堆,再一个一个删除把值赋给数组即可。
public static void heapSort(int[] nums) {
Heap<Integer> heap = new Heap<>();
for(int i = 0; i < nums.length; i++) {
heap.add(nums[i]);
}
for(int i = nums.length - 1; i >= 0; i--) {
nums[i] = heap.remove();
}
}
时间复杂度O(n*logn),空间复杂度O(1)