冒泡排序(重要),ac=(n^2, 1)
稳定
通过不断的比较,将最大值放入数组末尾。(数组末尾指针递减)
// Java
import java.util.Arrays;
public class HelloWorld {
public static void main(String []args) {
int[] arr = new int[] {1, 2, 5, 2, 6, 7, 4, 3};
for(int i = 0; i < arr.length; ++i) {
for(int j = 0; j < arr.length - i - 1; ++j){
if(arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
# Python
def bubbleSort(arr):
for i in range(1, len(arr)):
for j in range(0, len(arr)-i):
if arr[j] > arr[j+1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
print(bubbleSort([1,5,3,2,6,4]))
选择排序,ac=(n^2, 1)
不稳定
选择排序是按数组顺序遍历,然后将后续最小的值与当前遍历值进行交换(数组头部指针递增)
// Java
import java.util.Arrays;
public class HelloWorld {
public static void main(String []args) {
int[] arr = new int[] {1, 2, 5, 2, 6, 7, 4, 3};
for(int i = 0; i < arr.length; ++i) {
int index = i;
for(int j = i + 1; j < arr.length; ++j){
if(arr[j] < arr[index])
index = j;
}
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
System.out.println(Arrays.toString(arr));
}
}
# Python
def selectionSort(arr):
for i in range(len(arr) - 1):
# 记录最小数的索引
minIndex = i
for j in range(i + 1, len(arr)):
if arr[j] < arr[minIndex]:
minIndex = j
# i 不是最小数时,将 i 和最小数进行交换
if i != minIndex:
arr[i], arr[minIndex] = arr[minIndex], arr[i]
return arr
print(bubbleSort([1,5,3,2,6,4]))
插入排序(重要),ac=(n^2, 1)
稳定
插入排序就是按数组顺序将每个值向前比较并插入到其应该的位置(数组头部指针递增)
// Java
import java.util.Arrays;
public class HelloWorld {
public static void main(String []args) {
int[] arr = new int[] {1, 2, 5, 2, 6, 7, 4, 3};
for(int i = 0; i < arr.length; ++i) {
int index = i - 1;
int current = arr[i];
while(index >= 0 && current < arr[index]) {
arr[index + 1] = arr[index];
index--;
}
arr[index + 1] = current;
}
System.out.println(Arrays.toString(arr));
}
}
# Python
def insertionSort(arr):
for i in range(len(arr)):
preIndex = i-1
current = arr[i]
while preIndex >= 0 and arr[preIndex] > current:
arr[preIndex+1] = arr[preIndex]
preIndex-=1
arr[preIndex+1] = current
return arr
print(insertionSort([1,5,3,2,6,4]))
希尔排序,ac=(n^1.3, 1)
不稳定
希尔排序是分组的插入排序,不会导致数组内元素大范围的移动,但是需要进行多次遍历
// Java
import java.util.Arrays;
public class HelloWorld {
public static void main(String []args) {
int[] arr = new int[] {1, 2, 5, 2, 6, 7, 4, 3};
int le = arr.length;
for(int gap = le / 2;gap > 0; gap /= 2) { //gap对le依次减半
for(int i = gap; i < le; i++){ //这里需要对每个数往前(gap步数)插入
int j = i;
int current = arr[i];
//这里与插入排序一样,只是每次的比较步数是gap
while(j - gap >= 0 && current < arr[j - gap]) {
arr[j] = arr[j - gap];
j = j - gap;
}
arr[j] = current;
}
}
System.out.println(Arrays.toString(arr));
}
}
java算法:刚开始分为2个组,此时步长为len(arr)/2,之后按照步长一次缩小为一半
python算法:刚开始动态设置步长,基数为3
# Python
def shellSort(arr):
import math
gap=1
while(gap < len(arr)/3):
gap = gap*3+1
while gap > 0:
for i in range(gap,len(arr)):
temp = arr[i]
j = i-gap
while j >=0 and arr[j] > temp:
arr[j+gap]=arr[j]
j-=gap
arr[j+gap] = temp
gap = math.floor(gap/3)
return arr
print(shellSort([1,5,3,2,6,4]))
归并排序(重要),ac=(nlogn, n)
稳定
先将数组分成两部分递归,然后再回溯,将两部分排好序之后合并
// Java
import java.util.Arrays;
class MergSort{
public static int[] merg(int[] nums, int l, int r) {
if(l == r)
return new int[] {l};
int mid = l + (r - l) / 2;
int[] leftArrays = merg(nums, l, mid);
int[] rightArrays = merg(nums, mid + 1, r);
int[] newArrays = new int[leftArrays.length + rightArrays.length];
int m = 0, i = 0, j = 0;
while(i < leftArrays.length && j < rightArrays.length) {
newArrays[m++] = leftArrays[i] <= rightArrays[j] ? leftArrays[i++] : rightArrays[j++];
}
while(i < leftArrays.length) {
newArrays[m++] = leftArrays[i++];
}
while(j < rightArrays.length) {
newArrays[m++] = rightArrays[j++];
}
return newArrays;
}
public static void main(String[] args) {
int[] test = new int[] {3,5,2,5,2,7,4,8};
System.out.println(Arrays.toString(merg(test, 0, test.length - 1)));
}
}
# Python
# 归并排序
#这是合并的函数
# 将序列L[first...mid]与序列L[mid+1...last]进行合并
def mergearray(L,first,mid,last,temp):
#对i,j,k分别进行赋值
i,j,k = first,mid+1,0
#当左右两边都有数时进行比较,取较小的数
while (i <= mid) and (j <= last):
if L[i] <= L[j]:
temp[k] = L[i]
i = i+1
k = k+1
else:
temp[k] = L[j]
j = j+1
k = k+1
#如果左边序列还有数
while (i <= mid):
temp[k] = L[i]
i = i+1
k = k+1
#如果右边序列还有数
while (j <= last):
temp[k] = L[j]
j = j+1
k = k+1
#将temp当中该段有序元素赋值给L待排序列使之部分有序
for x in range(0,k):
L[first+x] = temp[x]
# 这是分组的函数
def merge_sort(L,first,last,temp):
if first < last:
mid = (int)((first + last) / 2)
#使左边序列有序
merge_sort(L,first,mid,temp)
#使右边序列有序
merge_sort(L,mid+1,last,temp)
#将两个有序序列合并
mergearray(L,first,mid,last,temp)
# 归并排序的函数
def merge_sort_array(L):
#声明一个长度为len(L)的空列表
temp = len(L)*[None]
#调用归并排序
merge_sort(L,0,len(L)-1,temp)
return L
print(merge_sort_array([1,5,3,2,6,4]))
快速排序(重要),ac=(nlogn,nlogn)
不稳定
每次确定当前数组[0]的最终位置,以最终位置为分界,递归查找左分数组和右分数组[0]的最终位置,因为需要递归到每个数,并且每个数的处理是logn,因此时间空间都是nlogn
// Java
import java.util.Arrays;
public class HelloWorld {
public static void main(String []args) {
int[] arr = new int[] {1, 2, 5, 2, 6, 7, 4, 3};
quick_sort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quick_sort(int[] arr, int begin, int end) {
//如果
if(begin >= end)
return;
//first和last记录当前分组的首位位置
int first = begin, last = end;
int pivot = arr[begin]; //记录中心点
while(begin < end) {
//判断end
while(begin < end){
if(pivot <= arr[end])
end -= 1;
else {
arr[begin] = arr[end];
begin += 1;
break;
}
}
//判断begin
while(begin < end){
if(pivot >= arr[begin])
begin += 1;
else {
arr[end] = arr[begin];
end -= 1;
break;
}
}
}
arr[begin] = pivot; //将begin位置换为pivot
quick_sort(arr, first, begin - 1); //递归begin左分组
quick_sort(arr, end + 1, last); //递归end右分组
}
}
# Python
#快速排序
#L:待排序的序列;start排序的开始index,end序列末尾的index
#对于长度为length的序列:start = 0;end = length-1
def quick_sort(L,start,end):
if start < end:
i , j , pivot = start , end , L[start]
while i < j:
#从右开始向左寻找第一个小于pivot的值
while (i < j) and (L[j] >= pivot):
j = j-1
#将小于pivot的值移到左边
if (i < j):
L[i] = L[j]
i = i+1
#从左开始向右寻找第一个大于pivot的值
while (i < j) and (L[i] < pivot):
i = i+1
#将大于pivot的值移到右边
if (i < j):
L[j] = L[i]
j = j-1
#循环结束后,说明 i=j,此时左边的值全都小于pivot,右边的值全都大于pivot
#pivot的位置移动正确,那么此时只需对左右两侧的序列调用此函数进一步排序即可
#递归调用函数:依次对左侧序列:从0 ~ i-1//右侧序列:从i+1 ~ end
L[i] = pivot
#左侧序列继续排序
quick_sort(L,start,i-1)
#右侧序列继续排序
quick_sort(L,i+1,end)
L = [1,5,3,2,6,4]
quick_sort(L, 0, len(L) - 1)
print(L)
堆排序,ac=(nlogn,1)
不稳定
在大根堆的基础上每次将最大值找出来,然后放在最后,并对之前的再找出最大值,与冒泡排序思路相同,只是时间复杂度降低,但是不是稳定的
// Java
import java.util.Arrays;
public class HeapSort {
public static void main(String []args){
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int []arr){
//1.构建大顶堆
for(int i=arr.length/2-1;i>=0;i--){
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for(int j=arr.length-1;j>0;j--){
swap(arr,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr,0,j);//重新对堆进行调整
}
}
/**
* 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
* @param arr
* @param i
* @param length
*/
public static void adjustHeap(int []arr,int i,int length){
int temp = arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
k++;
}
if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
/**
* 交换元素
* @param arr
* @param a
* @param b
*/
public static void swap(int []arr,int a ,int b){
int temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
# Python
#-------------------------堆排序--------------------------------
#**********获取左右叶子节点**********
def LEFT(i):
return 2*i + 1
def RIGHT(i):
return 2*i + 2
#********** 调整大顶堆 **********
#L:待调整序列 length: 序列长度 i:需要调整的结点
def adjust_max_heap(L,length,i):
#定义一个int值保存当前序列最大值的下标
largest = i
#执行循环操作:两个任务:1 寻找最大值的下标;2.最大值与父节点交换
while (1):
#获得序列左右叶子节点的下标
left,right = LEFT(i),RIGHT(i)
#当左叶子节点的下标小于序列长度 并且 左叶子节点的值大于父节点时,将左叶子节点的下标赋值给largest
if (left < length) and (L[left] > L[i]):
largest = left
print('左叶子节点')
else:
largest = i
#当右叶子节点的下标小于序列长度 并且 右叶子节点的值大于父节点时,将右叶子节点的下标值赋值给largest
if (right < length) and (L[right] > L[largest]):
largest = right
print('右叶子节点')
#如果largest不等于i 说明当前的父节点不是最大值,需要交换值
if (largest != i):
temp = L[i]
L[i] = L[largest]
L[largest] = temp
i = largest
print(largest)
continue
else:
break
#********** 建立大顶堆 **********
def build_max_heap(L):
length = len(L)
for x in range((int)((length-1)/2),-1,-1):
adjust_max_heap(L,length,x)
#********** 堆排序 **********
def heap_sort(L):
#先建立大顶堆,保证最大值位于根节点;并且父节点的值大于叶子结点
build_max_heap(L)
#i:当前堆中序列的长度.初始化为序列的长度
i = len(L)
#执行循环:1. 每次取出堆顶元素置于序列的最后(len-1,len-2,len-3...)
# 2. 调整堆,使其继续满足大顶堆的性质,注意实时修改堆中序列的长度
while (i > 0):
temp = L[i-1]
L[i-1] = L[0]
L[0] = temp
#堆中序列长度减1
i = i-1
#调整大顶堆
adjust_max_heap(L,i,0)
L = [1,5,3,2,6,4]
heap_sort(L)
print(L)
计数排序,ac=(n + k,n + k)
稳定,n为输入数组长度,k为数组中最大最小值的差
计数排序不是比较类排序,作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
当k不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。
// Java
import java.util.Arrays;
public class HelloWorld {
public static void main(String []args) {
//排序的数组
int a[]={100,93,97,92,96,99,92,89,93,97,90,94,92,95};
int b[]=countSort(a);
System.out.println(Arrays.toString(b));
}
public static int[] countSort(int[]a){
int b[] = new int[a.length];
int max = a[0],min = a[0];
for(int i:a){
if(i>max){
max=i;
}
if(i<min){
min=i;
}
} //这里k的大小是要排序的数组中,元素大小的极值差+1
int k=max-min+1;
int c[]=new int[k];
for(int i=0;i<a.length;++i){
c[a[i]-min]+=1; //优化过的地方,减小了数组c的大小
}
int j = 0;
for(int i = 0; i < c.length; ++i) {
while(c[i] > 0) {
b[j++] = i + min;
c[i]--;
}
}
return b;
}
}
# Python
def count_sort(input_list):
length = len(input_list)
if length < 2:
return input_list
max_num = max(input_list)
count = [0] * (max_num + 1)
for element in input_list:
count[element] += 1
output_list = []
for i in range(max_num + 1):
for j in range(count[i]): # count[i]表示元素i出现的次数,如果有多次,通过循环重复追加
output_list.append(i)
return output_list
L = [1,5,3,2,6,4]
print(count_sort(L))
桶排序,ac=(n + k, n + k)
稳定
加入映射的计数排序,再计数排序当中会有很多没有值的计数,这占用了很大的数组空间。再桶排序桶进行优化,分装成桶,桶与桶之间有序,其次对桶内部进行排序,就会使得整个数组有序。
缺点:但是与计数排序一样,桶大小的设定仍然要结合具体应用场景,否则也会导致存在很多的空桶,使得复杂度增大
// Java
import java.util.Arrays;
import java.util.List;
public class HelloWorld {
public static void main(String []args) {
int a[]={100,93,97,92,96,99,92,89,93,97,90,94,92,95};
int b[]=bucketSort(a, 5);
System.out.println(Arrays.toString(b));
}
//插入排序算法
public static void insert_sort(List<Integer> arr) {
for(int i = 0; i < arr.size(); ++i) {
int index = i - 1;
int current = arr.get(i);
while(index >= 0 && current < arr.get(index)) {
arr.set(index + 1, arr.get(index));
index--;
}
arr.set(index + 1, current);
}
}
//桶排序
public static int[] bucketSort(int[] arr, int bucketSize) {
if (arr.length == 0) {
return arr;
}
int minValue = arr[0];
int maxValue = arr[0];
for (int i : arr) {
if (i < minValue) {
minValue = i; // 输入数据的最小值
}
if (i > maxValue) {
maxValue = i; // 输入数据的最大值
}
}
// 桶的初始化
int bucketCount = ((maxValue - minValue) / bucketSize) + 1;
List<Integer>[] buckets = new List[bucketCount];
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new ArrayList<Integer>();
}
// 利用映射函数将数据分配到各个桶中
for (int i = 0; i < arr.length; i++) {
buckets[((arr[i] - minValue) / bucketSize)].add(arr[i]);
}
int index = 0;
for (int i = 0; i < buckets.length; i++) {
insert_sort(buckets[i]); // 对每个桶进行排序,这里使用了插入排序
for (int j = 0; j < buckets[i].size(); j++) {
arr[index++] = (buckets[i].get(j));
}
}
return arr;
}
}
基数排序,ac=(n*k,n+k)
稳定,n位数组的长度,k为最大数的位数
基数排序根据数组中每个数字得位进行排序,每次将所排序的位按照从小到大排序让如,最后就会将数组排序。运用了桶的思想,只是这里将每一位按照0-9放入位桶中,再按桶取出排序,接着将高位按大小放入桶中,不断重复,知道最高位也排好序,然后按顺序取出即可(这里注意从桶中取出时应从0开始取)
// Java
import java.util.Arrays;
public class HelloWorld {
public static void radix_sort(int[] number, int d){ //d表示最大的数有多少位
int k = 0;
int n = 1;
int m = 1; //控制键值排序依据在哪一位
int[][] temp = new int[10][number.length]; //数组的第一维表示可能的余数0-9
int[] order = new int[10]; //数组order[i]用来表示该位是i的数的个数
while(m <= d){
for(int i = 0; i < number.length; i++){
int lsd = ((number[i] / n) % 10);
temp[lsd][order[lsd]] = number[i];
order[lsd]++;
}
for(int i = 0; i < 10; i++){
if(order[i] != 0){
for(int j = 0; j < order[i]; j++){
number[k] = temp[i][j];
k++;
}
}
order[i] = 0;
}
n *= 10;
k = 0;
m++;
}
}
public static void main(String[] args){
int[] data ={73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100};
radix_sort(data, 3);
System.out.println(Arrays.toString(data));
}
}