十大排序算法总结(附Java,Python代码)

冒泡排序(重要),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));
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值