详解排序算法(附带Java/Python/Js源码)

冒泡算法

        依次比较两个相邻的子元素,如果他们的顺序错误就把他们交换过来,重复地进行此过程直到没有相邻元素需要交换,即完成整个冒泡,时间复杂度O(n^{2})

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  3. 针对所有的元素重复以上的步骤,除了最后一个;
  4. 重复步骤1~3,直到排序完成。

        

Java 

	/*** 冒泡排序* @param array* @return*/public static int[] bubbleSort(int[] array){if(array.length > 0){for(int i = 0;i<array.length;i++){for(int j = 0;j<array.length - 1 - i;j++){if(array[j] > array[j+1]){int temp = array[j];array[j] = array[j+1];array[j+1] = temp;}}}}return array;}

         优化后,可减少循环次数

        //定义标志位,用于判定最外层每一轮是否进行了交换boolean flag = false;for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {//进入这个if分支里边,则说明有元素进行了交换//所以将flag=trueflag = true;int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}//在进行一轮的排序后,判断是否发生了元素是否交换;if (!flag) {// 没有交换,数组已是有序,则结束排序;break;} else {//如果发生交换,将flag再次置为false,进行下一轮排序flag = false;}}

Python 

from typing import List# 冒泡排序
def bubble_sort(arr: List[int]):"""冒泡排序(Bubble sort):param arr: 待排序的List,此处限制了排序类型为int:return: 冒泡排序是就地排序(in-place)"""length = len(arr)if length <= 1:returnfor i in range(length):'''设置标志位,若本身已经有序,则直接break'''is_made_swap = Falsefor j in range(length - i - 1):if arr[j] > arr[j + 1]:arr[j], arr[j + 1] = arr[j + 1], arr[j]is_made_swap = Trueif not is_made_swap:breakreturn arrif __name__ == '__main__':arr = bubble_sort([55, 34, 55, 33, 13, 45, 67])print(arr)

Js 

for(var i=0;i<arr.length-1;i++){//确定轮数for(var j=0;j<arr.length-i-1;j++){//确定每次比较的次数if(arr[j]>arr[j+1]){tem = arr[j];arr[j] = arr[j+1];arr[j+1] = tem;}}}

选择排序

        每一趟从待排序数组中选出最小的数字,按顺序放在已经排好序的数组的后面,直到全部排完。 时间复杂度:O(n^{2}) 空间复杂度:O(1)(常规)

  1. 初始状态:无序区为R[1..n],有序区为空;
  2. 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
  3. n-1趟结束,数组有序化了 
选择排序

Java

private static Integer[] selectSort(Integer[] arr) {/*初始化左端、右端元素索引*/int left = 0;int right = arr.length - 1;while (left < right) {/*初始化最小值、最大值元素的索引*/int min = left;int max = right;for (int i = left; i <= right; i++) {/*标记每趟比较中最大值和最小值的元素对应的索引min、max*/if (arr[i] < arr[min])min = i;if (arr[i] > arr[max])max = i;}/*最大值放在最右端*/int temp = arr[max];arr[max] = arr[right];arr[right] = temp;/*此处是先排最大值的位置,所以得考虑最小值(arr[min])在最大位置(right)的情况*/if (min == right)min = max;/*最小值放在最左端*/temp = arr[min];arr[min] = arr[left];arr[left] = temp;/*每趟遍历,元素总个数减少2,左右端各减少1,left和right索引分别向内移动1*/left++;right--;}return arr;}// 测试public static void main(String[] args) {Integer[] arr = new Integer[]{77, 22, 3, 4, 1, 44, 34, 41};Integer[] arrs = selectSort(arr);for (Integer integer : arrs) {System.out.println(integer);}}

Python 

def selectionSort(A):le = len(A)for i in range(le):  # 遍历次数for j in range(i + 1, le):  # 查找待排序序列中的最小元素并交换if A[i] > A[j]:A[i], A[j] = A[j], A[i]print(A)tes = [22, 41, 55, 46, 4, 5, 3, 32, 454]
selectionSort(tes)  # [3, 4, 5, 22, 32, 41, 46, 55, 454]

Js 

var arr = [2, 8, 6, 2, 9, 7, 3, 11, 6]//从第0次开始for (var j = 0; j < arr.length - 1; j++) {var minIndex = jfor (var i = j + 1; i < arr.length; i++) {if (arr[i] < arr[minIndex]) minIndex = i}if (minIndex != j) {var tmp = arr[j]arr[j] = arr[minIndex]arr[minIndex] = tmp}
}console.log(arr)

插入排序

         将一个记录插入到已经排序好的有序表中,从而得到一个新的、记录增加1的有序表。在实现过程中使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,进行移动。时间复杂度:O(n^{2}) 空间复杂度:O(1)(常规)

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 将新元素插入到该位置后;
  6. 重复步骤2~5。

Java

 直接插入排序

public static void insertSort2 (int[] numbers) {//控制循环轮数for (int i = 1; i < numbers.length; i++) {int temp = numbers[i]; //定义待交换元素int j; //定义待插入的位置for (j = i; j > 0 && temp < numbers[j - 1]; j --) {numbers[j] = numbers[j - 1];}numbers[j] = temp;System.out.println("第" + i + "轮的排序结果为:" + Arrays.toString(numbers));}System.out.println("排序后的结果为:" + Arrays.toString(numbers));}

 折半插入排序

           在直接插入排序的基础上,将查找方法从顺序查找改为折半查找,就是在将 将要插入到现有 有序序列的数字寻找适当插入位置的比较次数减少了。

 private static void bInsertSort(int[] s){for(int i=1;i<s.length;i++){int temp=s[i];//保存要插入的数的数值int low=0;//设置查找区间初始值 下区间值int high=i-1;//上区间值while(low<=high){//查找结束条件int mid=(high+low)/2;//折半,中间值if(s[mid]>temp){//待插入值小于中间值high=mid-1;//上区间值变为中间值-1}else {//待插入值大于中间值low=mid+1;//下区间值变为中间值+1}}for (int j=i-1;j>=low;j--){s[j+1]=s[j];//将low及low之前的数向前移动一位}s[low]=temp;//low就是待插入值的适当插入点}System.out.println(Arrays.toString(s));//输出排好序的数组}

Python

import pandas as pd
import random
import timeimport seaborn as sns
import matplotlib.pyplot as plt# 解决win 系统中文不显示问题
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']def insert_sort(tg_list):''' 排序算法:插入排序 '''for i in range(1,len(tg_list)):for j in range(i,0,-1):if tg_list[j] < tg_list[j-1]:tg_list[j-1], tg_list[j] = tg_list[j], tg_list[j-1]else:breakdef get_runtime(n):''' 获取排序耗时 '''time_start = time.clock()  # 记录开始时间list_ = [random.randint(1,n) for i in range(n)]insert_sort(list_)time_end = time.clock()  # 记录结束时间time_sum = time_end - time_start  # 计算的时间差为程序的执行时间,单位为秒/sreturn [n,time_sum]def plot(df):''' 绘制折线图 '''pd.options.display.notebook_repr_html=False  # 表格显示plt.rcParams['figure.dpi'] = 75  # 图形分辨率sns.set_style(style='darkgrid')  # 图形主题plt.figure(figsize=(4,3),dpi=150)sns.lineplot(data=df,x='数量',y='耗时(s)')if __name__ == "__main__":nums = range(100,10000,100)res = list(map(get_runtime,nums))res_df = pd.DataFrame(res,columns=['数量','耗时(s)'])plot(res_df)

Js 

   function insertSort(arr) {var len =arr.length;for (var i=1;i<len; i++) {var temp=arr[i];var j=i-1;//默认已排序的元素while (j>=0 && arr[j]>temp) {  //在已排序好的队列中从后向前扫描arr[j+1]=arr[j]; //已排序的元素大于新元素,将该元素移到一下个位置j--;}arr[j+1]=temp;}return arr}

 二分插入排序

function binaryInsertSort(arr) {var len =arr.length;for (var i=1;i<len; i++) {var key=arr[i],left=0,right=i-1;while(left<=right){       //在已排序的元素中二分查找第一个比它大的值var mid= parseInt((left+right)/2); //二分查找的中间值if(key<arr[mid]){ //当前值比中间值小  则在左边的子数组中继续寻找   right = mid-1;}else{left=mid+1;//当前值比中间值大   在右边的子数组继续寻找}}              for(var j=i-1;j>=left;j--){arr[j+1]=arr[j];}arr[left]=key;}return arr;}    

快速排序

         每趟排序时选出一个基准值,然后将所有元素与该基准值比较,并按大小分成左右两堆,然后递归执行该过程,直到所有元素都完成排序。

  1. 从数列中挑出一个元素,称为 “基准”(pivot);
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序 

Java

    ##--------------------------第一版----------------------------------------public static void quickSort1(int[] arr) {if (arr == null || arr.length < 2) {return;}process1(arr, 0, arr.length - 1);}//第一版快排,这一版的时间复杂度为O(n2)public static void process1(int[] arr, int L, int R) {if (L >= R) {return;}//在[L,R]范围上,根据arr[R],模仿netherlandsFlag方法,//将数组分为三个部分:<=arr[R]的部分,arr[R]本身,>=//arr[R]的部分,这样确定了在最终的数组中arr[R]的位置,//并返回这个位置int M = partition(arr, L, R);//接下来只要在左右两侧重复这个过程就行process1(arr, L, M - 1);process1(arr, M + 1, R);}public static int partition(int[] arr, int L, int R) {if (L > R) {return -1;}if (L == R) {return L;}int lessEqual = L - 1;int index = L;while (index < R) {if (arr[index] <= arr[R]) {swap(arr, index, ++lessEqual);}index++;}swap(arr, ++lessEqual, R);return lessEqual;}##--------------------------第二版----------------------------------------public static void quickSort2(int[] arr) {if (arr == null || arr.length < 2) {return;}process2(arr, 0, arr.length - 1);}//第二版快排:相比第一版的区别/优势:在于一次可以确定//相等基准值的一个区域,而不再只是一个值//第二版的时间复杂度为O(n2)public static void process2(int[] arr, int L, int R) {if (L >= R) {return;}int[] equalArea = netherlandsFlag(arr, L, R);process2(arr, L, equalArea[0] - 1);process2(arr, equalArea[1] + 1, R);}//这个方法的目的是按arr[R]为基准值,将arr数组划分为小于基准值、//等于基准值、大于基准值三个区域//在arr[L...R]上,以arr[R]做基准值,在[L...R]范围内,<arr[R]的数//都放在左侧,=arr[R]的数放在中间,>arr[R]的数放在右边//返回的值为和arr[R]相等的范围的数组public static int[] netherlandsFlag(int[] arr, int L, int R) {if (L > R) {return new int[] { -1, -1 };}if (L == R) {return new int[] { L, R };}//小于基准值的区域的右边界int less = L - 1;//大于基准值的区域的左边界int more = R;int index = L;while (index < more) {//等于基准值,不做处理,判断下一个数据if (arr[index] == arr[R]) {index++;//当前数小于基准值} else if (arr[index] < arr[R]) {//将当前值和小于区域右边的一个值交换:swap//判断下一个数据:index++//小于区域右移:++less(先++是为了完成swap)swap(arr, index++, ++less);} else {//将当前值和大于区域左边的一个值交换:swap//大于区域左移:--more(先--是为了完成swap)swap(arr, index, --more);}}//因为最开始是把arr[R]作为基准值的,所以在进行接下来的一步之前,//arr[R]实际是在大于区域的最右侧的,所以还需要进行一步交换,这样//整个数组就成了小于区域、等于区域、大于区域的样子swap(arr, more, R);//less + 1是等于区域的起始位置,more经过上一步的和arr[R]交换后,//就成了等于区域的结束位置return new int[] { less + 1, more };}##--------------------------第三版----------------------------------------public static void quickSort3(int[] arr) {if (arr == null || arr.length < 2) {return;}process3(arr, 0, arr.length - 1);}//第三版快排:不再固定去arr[R],改为去随机位置的值,然后//和arr[R]进行交换,接下来的过程就和第二版一样//第三版的复杂度变化了,是O(nlogn),该事件复杂度是最终求//的是数学上的一个平均期望值public static void process3(int[] arr, int L, int R) {if (L >= R) {return;}swap(arr, L + (int) (Math.random() * (R - L + 1)), R);int[] equalArea = netherlandsFlag(arr, L, R);process3(arr, L, equalArea[0] - 1);process3(arr, equalArea[1] + 1, R);}public static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}

Python

# coding=utf-8
def quick_sort(array, start, end):if start >= end:returnmid_data, left, right = array[start], start, endwhile left < right:while array[right] >= mid_data and left < right:right -= 1array[left] = array[right]while array[left] < mid_data and left < right:left += 1array[right] = array[left]array[left] = mid_dataquick_sort(array, start, left-1)quick_sort(array, left+1, end)if __name__ == '__main__':array = [11, 14, 34, 7, 30, 14, 27, 45, 25, 5, 66, 11]quick_sort(array, 0, len(array)-1)print(array)

Js

function quickSort( arr ) {if(arr.length <= 1) return arr;const num = arr[0];let left = [], right = [];for(let i = 1;i < arr.length; i++) {if(arr[i]<=num) left.push(arr[i]);else right.push(arr[i]);}return quickSort(left).concat([num],quickSort(right));
}

堆排序

        利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为0(nlogn),它也是不稳定排序。

  1. 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
  2. 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
  3. 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

 

Java

 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];//先取出当前元素ifor(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 heapify(unsorted, index, heap_size):largest = indexleft_index = 2 * index + 1right_index = 2 * index + 2if left_index < heap_size and unsorted[left_index] > unsorted[largest]:largest = left_indexif right_index < heap_size and unsorted[right_index] > unsorted[largest]:largest = right_indexif largest != index:unsorted[largest], unsorted[index] = unsorted[index], unsorted[largest]heapify(unsorted, largest, heap_size)
def heap_sort(unsorted):n = len(unsorted)for i in range(n // 2 - 1, -1, -1):heapify(unsorted, i, n)for i in range(n - 1, 0, -1):unsorted[0], unsorted[i] = unsorted[i], unsorted[0]heapify(unsorted, 0, i)return unsortedif __name__ == '__main__':test = [4,3,2,1]print(heap_sort(test))

Js

/*调整函数@param arr 待排序的数组@param i 表示等待调整的那个非叶子节点的索引@param length 待调整长度
*/
function adjustHeap(arr,i,length){var notLeafNodeVal = arr[i]; //非叶子节点的值//for循环,k的初始值为当前非叶子节点的左孩子节点的索引//k = 2*k+1表示再往左子节点找for(var k=i*2+1;k<length;k=2*k+1){//如果k+1还在待调整的长度内,且右子树的值大于等于左子树的值//将k++,此时为当前节点的右孩子节点的索引if(k+1<length && arr[k]<arr[k+1]){k++;}//如果孩子节点大于当前非叶子节点if(arr[k]>notLeafNodeVal){arr[i] = arr[k]; //将当前节点赋值为孩子节点的值i = k;//将i赋值为孩子的值,再看其孩子节点是否有比它大的}else{break; //能够break的保证是:从左到右、从上到下进行调整//只要上面的不大于,下面的必不大于}}//循环结束后,将i索引处的节点赋值为之前存的那个非叶子节点的值arr[i] = notLeafNodeVal;
}//堆排序函数
function heapSort(arr){//进行第一次调整for(var i=parseInt(arr.length/2)-1;i>=0;i--){adjustHeap(arr,i,arr.length);}for(var j=arr.length-1;j>0;j--){/*进行交换:第一次调整的时候从左到右、从上到下的;交换时只是变动了堆顶元素和末尾元素,末尾元素不用去管,因为已经是之前长度最大的了,只需要把当前堆顶元素找到合适的位置即可*/var temp = arr[j];arr[j] = arr[0];arr[0] = temp;adjustHeap(arr,0,j);}
}var arr = [0,2,4,1,5];
console.log("排序前的数组:",arr);
heapSort(arr);
console.log("排序后的数组:",arr);

希尔排序

        插入排序的一种算法,先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。时间复杂度O(N^1.3)

  • 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  • 按增量序列个数k,对序列进行k 趟排序;
  • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

Java 

	   /*初始化划分增量*/int increment = len;	int j,temp,low,mid,high;/*每次减小增量,直到increment = 1*/while (increment > 1){	/*增量的取法之一:除三向下取整+1*/increment = increment/3 + 1;/*对每个按增量划分后的逻辑分组,进行直接插入排序*/for (int i = increment; i < len; ++i) {	low = 0;high = i-1;temp = arr[i];while(low <= high){mid = (low+high)/2;if(arr[mid]>temp){high = mid-1;}else{low = mid+1;}}j = i-increment;/*移动元素并寻找位置*/while(j >= high+1){	arr[j+increment] = arr[j];j -= increment;} /*插入元素*/arr[j+increment] = temp;	}}

Python

# 希尔排序
def shell_sort(input_list):# 希尔排序:三重循环,依次插入,直接插入法的优化版l = input_list # 简化参数名gaps = [701, 301, 132, 57, 23, 10, 4, 1] # Marcin Ciura's gap sequencefor gap in gaps:for i in range(gap, len(l)):insert_value = l[i] # 终止条件j = iwhile j >= gap and l[j - gap] > insert_value:l[j] = l[j - gap]j -= gapif j != i:l[j] = insert_value # 循环终止,插入值return l
'''# 另外一种写法
def shell_sort(input_list):# 希尔排序:三重循环,依次插入,直接插入法的优化版l = input_list # 简化参数名gap = len(l) // 2 # 长度取半while gap > 0:for i in range(gap, len(l)):insert_value = l[i] # 终止条件j = iwhile j >= gap and l[j - gap] > insert_value:l[j] = l[j - gap]j -= gapif j != i:l[j] = insert_value # 循环终止,插入值gap //= 2return l
'''if __name__ == '__main__':test = [4,3,2,1]print(shell_sort(test))

Js

function shellSort(arr) {let len = arr.length;for (let gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {for (let i = gap; i < len; i++) {let j = i;let current = arr[i];while (j - gap >= 0 && arr[j - gap] > current) {arr[j] = arr[j - gap];j = j - gap;}arr[j] = current;}}return arr;
}

归并排序

        归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子 序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序 表,称为二路归并。时间复杂度为O(nlogn)

  • 把长度为n的输入序列分成两个长度为n/2的子序列;
  • 对这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列。

Java 

public class Merge02 {private static Comparable[] assist;public void sort(Comparable[] arr) {assist = new Comparable[arr.length];sort(arr, 0, arr.length - 1);}public void sort(Comparable[] arr, int lo, int hi) {if (lo >= hi) {return;}int mid = lo + (hi - lo) / 2;sort(arr, lo, mid);sort(arr, mid + 1, hi);merge(arr, lo, mid, hi);}public void merge(Comparable[] arr, int lo, int mid, int hi) {int i = lo;int p1 = lo;int p2 = mid + 1;while (p1 <= mid && p2 <= hi) {if (arr[p1].compareTo(arr[p2]) > 0) {assist[i++] = arr[p2++];} else {assist[i++] = arr[p1++];}}while (p1 <= mid) {assist[i++] = arr[p1++];}while (p2 <= hi) {assist[i++] = arr[p2++];}for (int index = lo; index <= hi; index++) {arr[index] = assist[index];}}
}

Python

#merge的功能是将前后相邻的两个有序表归并为一个有序表的算法。
def merge(left, right):ll, rr = 0, 0result = []while ll < len(left) and rr < len(right):if left[ll] < right[rr]:result.append(left[ll])ll += 1else:result.append(right[rr])rr += 1result+=left[ll:]result+=right[rr:]return resultdef merge_sort(alist):if len(alist) <= 1:return alistnum = len(alist) // 2   # 从中间划分两个子序列left = merge_sort(alist[:num]) # 对左侧子序列进行递归排序right = merge_sort(alist[num:]) # 对右侧子序列进行递归排序return merge(left, right) #归并tesl=[1,3,45,23,23,12,43,45,33,21]
print(merge_sort(tesl))
#[1, 3, 12, 21, 23, 23, 33, 43, 45, 45]

Js 

// 合并做分支数组和右分支数组,左分支和右分支在自己数组里面已经是有序的。所以就方便很多。
function merge(left, right){// 剪枝优化if(left[left.length -1] <= right[0]){return left.concat(right)}if(right[right.length-1] <= left[0]){return right.concat(left)}let tmp = [];let leftIndex =0, rightIndex = 0;while(leftIndex < left.length && rightIndex < right.length) {if(left[leftIndex] <= right[rightIndex]) {tmp.push(left[leftIndex]);leftIndex ++;}else{tmp.push(right[rightIndex]);rightIndex ++;}}// 最后,检查一下是否有数组内容没有拷贝完if(leftIndex < left.length){tmp = tmp.concat(left.slice(leftIndex))}if(rightIndex < right.length){tmp = tmp.concat(right.slice(rightIndex))}return tmp;
}function mergeSort(arr){// 拆到只有一个元素的时候,就不用再拆了。if(arr.length <2){return arr;}const mid = arr.length/2;const left = arr.slice(0, mid);const right = arr.slice(mid);const leftSplited = mergeSort(left);const rightSplited = mergeSort(right);return merge(leftSplited, rightSplited);
}

 基数排序(Radix Sort)

        基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。 基数排序的平均时间复杂度为 O(nk),k 为最大元素的长度,最坏时间复杂度为 O(nk),空间复杂度为 O(n) ,是稳定排序。

  1. 取得数组中的最大数,并取得位数,即为迭代次数n(例如:数组中最大数为123,则 n=3);
  2. arr为原始数组,从最低位(或最高位)开始根据每位的数字组成radix数组(radix数组是个二维数组,其中一维长度为10),例如123在第一轮时存放在下标为3的radix数组中;
  3. 将radix数组中的数据从0下标开始依次赋值给原数组;
  4. 重复2~3步骤n次即可。

Java

public static void radixSort(int[] arr) {if (arr.length < 2) return;int maxVal = arr[0];//求出最大值for (int a : arr) {if (maxVal < a) {maxVal = a;}}int n = 1;while (maxVal / 10 != 0) {//求出最大值位数maxVal /= 10;n++;}for (int i = 0; i < n; i++) {List<List<Integer>> radix = new ArrayList<>();for (int j = 0; j < 10; j++) {radix.add(new ArrayList<>());}int index;for (int a : arr) {index = (a / (int) Math.pow(10, i)) % 10;radix.get(index).add(a);}index = 0;for (List<Integer> list : radix) {for (int a : list) {arr[index++] = a;}}}}

Python

def radix_sort(input_list):RADIX = 10placement = 1max_digit = max(input_list)while placement <= max_digit:buckets: list[list] = [list() for _ in range(RADIX)]for i in input_list:tmp = int((i / placement) % RADIX)buckets[tmp].append(i)a = 0for b in range(RADIX):for i in buckets[b]:input_list[a] = ia += 1# move to nextplacement *= RADIXreturn input_listif __name__ == '__main__':test = [4,3,2,1]print(radix_sort(test))

Js 

function radixSort(array) {let length = array.length;// 如果不是数组或者数组长度小于等于1,直接返回,不需要排序if (!Array.isArray(array) || length <= 1) return;let bucket = [],max = array[0],loop;// 确定排序数组中的最大值for (let i = 1; i < length; i++) {if (array[i] > max) {max = array[i];}}// 确定最大值的位数loop = (max + '').length;// 初始化桶for (let i = 0; i < 10; i++) {bucket[i] = [];}for (let i = 0; i < loop; i++) {for (let j = 0; j < length; j++) {let str = array[j] + '';if (str.length >= i + 1) {let k = parseInt(str[str.length - 1 - i]); // 获取当前位的值,作为插入的索引bucket[k].push(array[j]);} else {// 处理位数不够的情况,高位默认为 0bucket[0].push(array[j]);}}array.splice(0, length); // 清空旧的数组// 使用桶重新初始化数组for (let i = 0; i < 10; i++) {let t = bucket[i].length;for (let j = 0; j < t; j++) {array.push(bucket[i][j]);}bucket[i] = [];}}return array;
}

 桶排序(Bucket Sort)

        计数排序的升级版。在额外空间充足的情况下,尽量增大桶的数量。使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中。 

        设置一个bucketSize(该数值的选择对性能至关重要,性能最好时每个桶都均匀放置所有数值,反之最差),表示每个桶最多能放置多少个数值;
        遍历输入数据,并且把数据依次放到到对应的桶里去;
        对每个非空的桶进行排序,可以使用其它排序方法(这里递归使用桶排序);
        从非空桶里把排好序的数据拼接起来即可。

Java

private static List<Integer> bucketSort(List<Integer> arr, int bucketSize) {int len = arr.size();if (len < 2 || bucketSize == 0) {return arr;}int minVal = arr.get(0), maxVal = arr.get(0);for (int i = 1; i < len; i++) {if (arr.get(i) < minVal) {minVal = arr.get(i);} else if (arr.get(i) > maxVal) {maxVal = arr.get(i);}}int bucketNum = (maxVal - minVal) / bucketSize + 1;List<List<Integer>> bucket = new ArrayList<>();for (int i = 0; i < bucketNum; i++) {bucket.add(new ArrayList<>());}for (int val : arr) {int idx = (val - minVal) / bucketSize;bucket.get(idx).add(val);}for (int i = 0; i < bucketNum; i++) {if (bucket.get(i).size() > 1) {bucket.set(i, bucketSort(bucket.get(i), bucketSize / 2));}}List<Integer> result = new ArrayList<>();for (List<Integer> val : bucket) {result.addAll(val);}return result;}

Python

def bucket_sort(input_list):l = input_listif not l:return []l_max, l_min = max(l), min(l)bucket_len = int(l_max - l_min) + 1buckets: list[list] = [[] for _ in range(bucket_len)]for i in l:buckets[int(i - l_min)].append(i)return [i for bucket in buckets for i in sorted(bucket)]if __name__ == '__main__':test = [4,3,2,1]print(bucket_sort(test))

Js 

function bucketSort(arr, bucketSize) {if (arr.length === 0) {return arr;}var i;var minValue = arr[0];var maxValue = arr[0];//求出最大值和最小值,这里有两种方式,两种方式只用其一,这里采用方式二。方式一://for (i = 1; i < arr.length; i++) {//  if (arr[i] < minValue) {//      minValue = arr[i];                // 输入数据的最小值// } else if (arr[i] > maxValue) {//     maxValue = arr[i];                // 输入数据的最大值//  }//  }//方式二:maxValue = Math.max(...arr);minValue = Math.min(...arr);//桶的初始化var DEFAULT_BUCKET_SIZE = 5;            // 设置桶的默认数量为5bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;  var buckets = new Array(bucketCount);for (i = 0; i < buckets.length; i++) {buckets[i] = [];}//利用映射函数将数据分配到各个桶中for (i = 0; i < arr.length; i++) {buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);}arr.length = 0;for (i = 0; i < buckets.length; i++) {insertionSort(buckets[i]);                      // 对每个桶进行排序,这里使用了插入排序,这里也可以直接用js的数组sort方法,很直接for (var j = 0; j < buckets[i].length; j++) {arr.push(buckets[i][j]);                      }}return arr;
}

 计数排序

        计数排序是一种非基于比较的排序算法,其空间复杂度和时间复杂度均为O(n+k),其中k是整数的范围。基于比较的排序算法时间复杂度最小是O(nlogn)的。

  1. 找出数组中的最大值maxVal和最小值minVal;
  2. 创建一个计数数组countArr,其长度是maxVal-minVal+1,元素默认值都为0;
  3. 遍历原数组arr中的元素arr[i],以arr[i]-minVal作为countArr数组的索引,以arr[i]的值在arr中元素出现次数作为countArr[a[i]-min]的值;
  4. 遍历countArr数组,只要该数组的某一下标的值不为0则循环将下标值+minVal输出返回到原数组即可。

Java

public static void countingSort(int[] arr) {int len = arr.length;if (len < 2) return;int minVal = arr[0], maxVal = arr[0];for (int i = 1; i < len; i++) {if (arr[i] < minVal) {minVal = arr[i];} else if (arr[i] > maxVal) {maxVal = arr[i];}}int[] countArr = new int[maxVal - minVal + 1];for (int val : arr) {countArr[val - minVal]++;}for (int arrIdx = 0, countIdx = 0; countIdx < countArr.length; countIdx++) {while (countArr[countIdx]-- > 0) {arr[arrIdx++] = minVal + countIdx;}}}

Python

def count_sort2(arr):maxi = max(arr)mini = min(arr)count_arr_length = maxi - mini + 1  # 计算待排序列表的数值区域长度,如4-9共9+1-4=6个数count_arr = [0 for _ in range(count_arr_length)]   # 创建一个全为0的列表for value in arr:count_arr[value - mini] += 1   # 统计列表中每个值出现的次数# 使count_arr[i]存放<=i的元素个数,就是待排序列表中比某个值小的元素有多少个for i in range(1, count_arr_length):count_arr[i] = count_arr[i] + count_arr[i - 1]new_arr = [0 for _ in range(len(arr))]   # 存放排序结果for i in range(len(arr)-1, -1, -1):    # 从后往前循环是为了使排序稳定new_arr[count_arr[arr[i] - mini] - 1] = arr[i]   # -1是因为下标是从0开始的count_arr[arr[i] - mini] -= 1   # 每归位一个元素,就少一个元素return new_arrif __name__ == '__main__':user_input = input('输入待排序的数,用\",\"分隔:\n').strip()#strip() 方法用于移除字符串头尾指定的字符(默认为空格)unsorted = [int(item) for item in user_input.split(',')]print(count_sort2(unsorted))

Js

function countingSort(arr, maxValue) {var bucket = new Array(maxValue+1),sortedIndex = 0;arrLen = arr.length,bucketLen = maxValue + 1;for (var i = 0; i < arrLen; i++) {if (!bucket[arr[i]]) {bucket[arr[i]] = 0;}bucket[arr[i]]++;}for (var j = 0; j < bucketLen; j++) {while(bucket[j] > 0) {arr[sortedIndex++] = j;bucket[j]--;}}return arr;
}
var arr = [2, 3, 8, 7, 1, 2, 7, 3];
console.log(countingSort(arr,8));//1,2,2,3,3,7,7,8
🌹 以上分享 10大常见排序算法,如有问题请指教写。🌹🌹 如你对技术也感兴趣,欢迎交流。🌹🌹🌹  如有需要,请👍点赞💖收藏🐱‍🏍分享 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/62386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为何电商行业都在争相使用WhatsApp引流小挂件?

WhatsApp小挂件是嵌入在网站上的聊天小部件&#xff0c;允许访问者同WhatsApp与您联系。点击后&#xff0c;它会将客户带到移动或桌面 WhatsApp应用程序&#xff0c;或者直接打开一个对话框&#xff0c;客户可以在这些地方与您发起对话。让我们看看在您的网站上拥有WhatsApp聊天…

常用激活函数整理

最近一边应付工作&#xff0c;一边在补足人工智能的一些基础知识&#xff0c;这个方向虽然新兴&#xff0c;但已是卷帙浩繁&#xff0c;有时不知从何入手&#xff0c;幸亏有个适合基础薄弱的人士学习的网站&#xff0c;每天学习一点&#xff0c;积跬步以至千里吧。有像我一样学…

PHP多语言代入电商平台api接口采集拼多多根据ID获取商品详情原数据示例

拼多多商品详情原数据API接口的作用是获取拼多多电商平台上某一商品的详细信息&#xff0c;包括商品的标题、价格、库存、图片、描述、包邮信息、销量、评价、优惠券等数据。通过该API接口可以获取到商品的原始数据&#xff0c;用于分析、筛选和展示商品信息。 pinduoduo.item…

复习之docker部署--项目实战

一、实验环境 1.安装7.6虚拟机 最小化安装&#xff0c;不安装图形&#xff01; 2.封装虚拟机 关闭selinux关闭防火墙关闭networkmanager配置网络&#xff0c;保证可以ssh修改主机名添加双向解析配置7.6网络仓库--安装常用的工具 配置完成后&#xff0c;在真机ssh虚拟机 如果…

在Mac终端使用unrar和rar 解压和压缩软件

1、首先从rarlab 网站下载 rar &#xff0f; unrar 工具 rarlab网站&#xff1a; https://www.rarlab.com/download.htm 2、解压缩下载的 tar.gz 压缩包&#xff08;rarmacos-x64-623.tar.gz&#xff09;&#xff0c;在下载目录downloads下自动创建一个rar的目录&#xff0c;其…

从零开始的Hadoop学习(四)| SSH无密登录配置、集群配置

1. SSH 无密登录配置 1.1 配置 ssh &#xff08;1&#xff09;基本语法 ssh 另一台电脑的IP地址 &#xff08;2&#xff09;ssh 连接时出现 Host key verification failed 的解决方法 [atguiguhadoop102 ~]$ ssh hadoop103&#xff08;3&#xff09;回退到 hadoop102 [at…

bazel入门学习笔记

简介 Bazel Google开源的&#xff0c;是一款与 Make、Maven 和 Gradle 类似的开源构建和测试工具。 它使用人类可读的高级构建语言。Bazel 支持多种语言的项目&#xff0c;可为多个平台构建输出。Bazel支持任意大小的构建目标&#xff0c;并支持跨多个代码库和大量用户的大型代…

登录校验-Filter-登录校验过滤器

目录 思路 登录校验Filter-流程 步骤 流程图 登录校验Filter-代码 过滤器类 工具类 测试登录 登录接口功能请求 其他接口功能请求 前后端联调 思路 前端访问登录接口&#xff0c;登陆成功后&#xff0c;服务端会生成一个JWT令牌&#xff0c;并返回给前端&#xff0…

【状态估计】基于UKF法、AUKF法的电力系统三相状态估计研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Unity Canvas动画不显示的问题

问题描述: 我通过角色创建了一个walk的动画&#xff0c;当我把这个动画给到Canvas里面的一个image上&#xff0c;这个动画就不能正常播放了&#xff0c;经过一系列的查看我才发现&#xff0c;canvas里面动画播放和非canvas得动画播放&#xff0c;他们的动画参数是不一样的。一个…

2D-2D对极几何中的基本矩阵、本质矩阵和单应矩阵

本文主要参考高翔博士的视觉SLAM十四讲第二版中的7.3章节内容。文章目录 1 对极约束2 本质矩阵E3 单应矩阵 1 对极约束 现在&#xff0c;假设我们从两张图像中得到了一对配对好的特征点&#xff0c;如图7.9所示&#xff08;假如后面我们有若干对这样的匹配点&#xff0c;根据这…

高效使用WMS仓储管理系统,需要关注这八个点

在现代供应链中&#xff0c;WMS仓储管理系统扮演着至关重要的角色。然而&#xff0c;随着供应链需求的不断增长和变化&#xff0c;实施WMS仓储管理系统面临着越来越多的挑战和要求。本文将探讨使用WMS仓储管理系统需要考虑的8大因素&#xff0c;以适应现代供应链的需求。 一、W…

树和二叉树基础

引言&#xff1a; 树是一种非线性的结构&#xff0c;也是由一个一个的结点构成。 树的一些基本概念&#xff1a; 节点的度&#xff1a;一个节点含有的子树的个数称为该节点的度&#xff1b;如上图&#xff1a;A的度为6 叶节点或终端节点&#xff1a;度为0的节点称为叶节点。…

优秀的ui设计作品(合集)

UI设计师需要了解的九个Tips 1.图片类APP排版突破 规则是死的&#xff0c;人是活的。很多时候&#xff0c;如果需求是比较宽要尝试突破原则&#xff0c;用一些另类的排版方式&#xff0c;其实也是做好设计的本质。在图片类app中&#xff0c;错落一些的排版会使你的作品更有魅力…

Metinfo6.0.0任意文件读取漏洞复现

漏洞原理 在\MetInfo6.0.0\app\system\include\module\的old_thumb.class.php文件 可以看到这里对./进行了严格的过滤&#xff0c;但是却忽略了在Windows下还可以用…\来跳转目录 环境搭建 下载Metinfo6.0.0 配置随便写&#xff0c;自己记住就行 这里前面已经审计过代码了&a…

基于YOLOv8分割模型实现垃圾识别

基于YOLOv8分割模型实现垃圾识别 本文首发于公众号【DeepDriving】&#xff0c;欢迎关注。 0. 引言 YOLOv8是Ultralytics开源的一个非常火的AI算法&#xff0c;目前支持目标检测、实例分割、姿态估计等任务。如果对YOLOv8的安装和使用还不了解的可以参考我之前写的这篇文章&am…

VUE环境下 CSS3+JS 实现发牌 翻牌

创建牌容器&#xff08;关键点&#xff1a;overflow&#xff1a;hidden&#xff09;&#xff1a; <div class"popup-box"></div> .popup-box {position: absolute;width: 100vw;height: 100vh;top: 0px;left: 0;overflow: hidden; } 创建每一张牌《固…

基于java swing和mysql实现的电影票购票管理系统(源码+数据库+运行指导视频)

一、项目简介 本项目是一套基于java swing和mysql实现的电影票购票管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都…

PHP小白搭建Kafka环境以及初步使用rdkafka

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装java&#xff08;Kafka必须安装java&#xff0c;因为kafka依赖java核心&#xff09;二、安装以及配置Kafka、zookeeper1.下载Kafka&#xff08;无需下载…

26 Linux高级篇-Linux面试题

26 Linux高级篇-Linux面试题 文章目录 26 Linux高级篇-Linux面试题1.分析日志t.txt(访问量)&#xff0c;将各个ip地址截取&#xff0c;并统计出现次数&#xff0c;并按从大到小排序(腾讯)2.统计连接到服务器的各个ip情况&#xff0c;并按连接数从大到小排序(腾讯)3.如忘记了mys…