详解排序算法(附带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=true
                    flag = 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:
        return

    for i in range(length):
        '''设置标志位,若本身已经有序,则直接break'''
        is_made_swap = False
        for 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 = True
        if not is_made_swap:
            break
    return arr


if __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 = j

  for (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 time

import 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:
                break
                
def 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  # 计算的时间差为程序的执行时间,单位为秒/s
    
    return [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:
        return
    mid_data, left, right = array[start], start, end
    while left < right:
        while array[right] >= mid_data and left < right:
            right -= 1
        array[left] = array[right]
        while array[left] < mid_data and left < right:
            left += 1
        array[right] = array[left]
    array[left] = mid_data
    quick_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];//先取出当前元素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 heapify(unsorted, index, heap_size):
        largest = index
        left_index = 2 * index + 1
        right_index = 2 * index + 2
        if left_index < heap_size and unsorted[left_index] > unsorted[largest]:
                largest = left_index
        if right_index < heap_size and unsorted[right_index] > unsorted[largest]:
                largest = right_index
        if 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 unsorted
 
if __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 sequence
        for gap in gaps:
                for i in range(gap, len(l)):
                        insert_value = l[i] # 终止条件
                        j = i
                        while j >= gap and l[j - gap] > insert_value:
                                l[j] = l[j - gap]
                                j -= gap
                        if 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 = i
                        while j >= gap and l[j - gap] > insert_value:
                                l[j] = l[j - gap]
                                j -= gap
                        if j != i:
                                l[j] = insert_value # 循环终止,插入值
                gap //= 2
        return 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, 0
    result = []
    while ll < len(left) and rr < len(right):
        if left[ll] < right[rr]:
            result.append(left[ll])
            ll += 1
        else:
            result.append(right[rr])
            rr += 1
    result+=left[ll:]
    result+=right[rr:]
    return result

def merge_sort(alist):
    if len(alist) <= 1:
        return alist
    num = 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 = 10
        placement = 1
        max_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 = 0
                for b in range(RADIX):
                        for i in buckets[b]:
                                input_list[a] = i
                                a += 1
                # move to next
                placement *= RADIX
        return input_list
 
if __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 {
        // 处理位数不够的情况,高位默认为 0
        bucket[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_list
        if not l:
                return []
        l_max, l_min = max(l), min(l)
        bucket_len = int(l_max - l_min) + 1
        buckets: 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;            // 设置桶的默认数量为5
    bucketSize = 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_arr


if __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大常见排序算法,如有问题请指教写。

🌹🌹 如你对技术也感兴趣,欢迎交流。

🌹🌹🌹  如有需要,请👍点赞💖收藏🐱‍🏍分享 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RSA算法与错误敏感攻击

参见《RSA 算法的错误敏感攻击研究与实践》 RSA 算法简介 RSA 算法原理&#xff1a; 1&#xff09; RSA 算法密钥产生过程 &#xff08;1&#xff09;系统随机产生两个大素数 p p p 和 q q q&#xff0c;对这两个数据保密&#xff1b; &#xff08;2&#xff09;计算 n p …

springboot集成es 插入和查询的简单使用

第一步&#xff1a;引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId><version>2.2.5.RELEASE</version></dependency>第二步&#xff1a;…

uniapp微信小程序使用stomp.js实现STOMP传输协议的实时聊天

简介&#xff1a; 原生微信小程序中使用 本来使用websocket&#xff0c;后端同事使用了stomp协议&#xff0c;导致前端也需要对应修改。 如何使用 1.yarn add stompjs 2.版本 “stompjs”: “^2.3.3” 3.在static/js中新建stomp.js和websocket.js&#xff0c;然后在需要使用…

Nginx详解 三:高级配置

文章目录 1. 网页的状态页2. Nginx第三方模块2.1 echo模块 3. 变量3.1 内置变量3.1.1 示例 3.2 自定义变量3.2.1 自定义访问日志3.2.2 自定义json 格式日志 3.4 Nginx压缩功能 4. HTTPS4.1 Nginx的HTTPS工作原理4.2 启用功能模块的配置过程 5、自定义图标 1. 网页的状态页 基于…

深度学习在自然语言处理中的十大应用领域

文章目录 1. 机器翻译2. 文本分类3. 命名实体识别4. 问答系统5. 文本生成6. 情感分析7. 语言生成与处理8. 信息检索与摘要9. 文本纠错与修复10. 智能对话系统总结 &#x1f389;欢迎来到AIGC人工智能专栏~深度学习在自然语言处理中的十大应用领域 ☆* o(≧▽≦)o *☆嗨~我是IT陈…

【Kali Linux高级渗透测试】深入剖析Kali Linux:高级渗透测试技术与实践

&#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于恒川的日常汇报系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏C语言初阶、C…

NPM 常用命令(一)

目录 1、npm 1.1 简介 1.2 依赖性 1.3 安装方式 2、npm access 2.1 命令描述 2.2 详情 3、npm adduser 3.1 描述 4、npm audit 4.1 简介 4.2 审计签名 4.3 操作示例 4.4 配置 audit-level dry-run force json package-lock-only omit foreground-scripts …

Ubuntu 下安装Qt5.12.12无法输入中文解决方法

Ubuntu 下安装Qt5.12.12无法输入中文解决方法 一&#xff0c;环境&#xff1a; &#xff08;1&#xff09;VMware Workstation 15 Pro &#xff08;2&#xff09;Ubuntu 20.04 &#xff08;3&#xff09;Qt 5.12.12 64bits &#xff08;4&#xff09;Qt Creator 5.0.2 &#…

浅析Redis(1)

一.Redis的含义 Redis可以用来作数据库&#xff0c;缓存&#xff0c;流引擎&#xff0c;消息队列。redis只有在分布式系统中才能充分的发挥作用&#xff0c;如果是单机程序&#xff0c;直接通过变量来存储数据是更优的选择。那我们知道进程之间是有隔离性的&#xff0c;那么re…

[第七届蓝帽杯全国大学生网络安全技能大赛 蓝帽杯 2023]——Web方向部分题 详细Writeup

Web LovePHP 你真的熟悉PHP吗&#xff1f; 源码如下 <?php class Saferman{public $check True;public function __destruct(){if($this->check True){file($_GET[secret]);}}public function __wakeup(){$this->checkFalse;} } if(isset($_GET[my_secret.flag]…

用AI + Milvus Cloud搭建着装搭配推荐系统教程

以下函数定义了如何将图像转换为向量并插入到 Milvus Cloud 向量数据库中。代码会循环遍历所有图像。(注意:如果需要开启 Milvus Cloud 全新特性动态 Schema,需要修改代码。) 查询向量数据库 以下代码演示了如何使用输入图像查询 Milvus Cloud 向量数据库,以检索和上传…

华为OD机试 - 符合要求的元组的个数 - 回溯(Java 2023 B卷 100分)

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 一、题目描述 给定一个整数数组nums、一个数字k&#xff0c;一个整数目标值target&#xff0c;请问nums中…

null和undefined区别

1.undefined&#xff0c;表示无值。 比如下面场景&#xff1a; a. 变量被声明了&#xff0c;但是没有被赋值&#xff1b; b. 调用函数的时候&#xff0c;应该给函数传参却没有给函数传这个参数打印出来就是 undefined&#xff1b; c. 访问一个对象中没有的属性&#xff1b;…

【LeetCode-中等题】24. 两两交换链表中的节点

文章目录 题目方法一&#xff1a;递归方法二&#xff1a;三指针迭代 题目 方法一&#xff1a;递归 图解&#xff1a; 详细版 public ListNode swapPairs(ListNode head) {/*递归法:宗旨就是紧紧抓住原来的函数究竟返回的是什么?作用是什么即可其余的细枝末节不要细究,编译器…

实战系列(一)| Dubbo和Spring Cloud的区别,包含代码详解

目录 1. 概述2. 核心功能3. 代码示例4. 适用场景 Dubbo 和 Spring Cloud 都是微服务架构中的重要框架&#xff0c;但它们的定位和关注点不同。Dubbo 是阿里巴巴开源的一个高性能、轻量级的 RPC 框架&#xff0c;主要用于构建微服务之间的服务治理。而 Spring Cloud 是基于 Spri…

数据结构与算法基础-学习-31-交换排序之冒泡排序、快速排序

排序的其他相关知识点和源码分享可以参考之前的博客&#xff1a; 《数据结构与算法基础-学习-30-插入排序之直接插入排序、二分插入排序、希尔排序》 一、交换排序基本思想 两两比较&#xff0c;如果发生逆序则交换位置&#xff0c;直到所有数据记录都排好序为止。 二、冒…

无涯教程-Android - Style Demo Example函数

下面的示例演示如何将样式用于单个元素。让我们开始按照以下步骤创建一个简单的Android应用程序- 步骤说明 1 您将使用Android Studio IDE创建一个Android应用程序,并在 com.example.saira_000.myapplication 包下将其命名为 myapplication ,如中所述您好世界Example一章。 2 …

常用Web漏洞扫描工具汇总(持续更新中)

常用Web漏洞扫描工具汇总 常用Web漏洞扫描工具汇总1、AWVS&#xff0c;2、OWASP Zed&#xff08;ZAP&#xff09;&#xff0c;3、Nikto&#xff0c;4、BurpSuite&#xff0c;5、Nessus&#xff0c;6、nmap7、X-ray还有很多不是非常知名&#xff0c;但可能也很大牌、也较常见的。…

el-date-picker限制选择的时间范围

<el-date-pickersize"mini"v-model"dateTime"value-format"yyyy-MM-dd HH:mm:ss"type"datetimerange"range-separator"~"start-placeholder"开始日期"end-placeholder"结束日期":picker-options&quo…

Fooocus启动时modules报错的解决方法

原理&#xff1a;是由于其他程序的安装导致modules的版本不对&#xff0c;先卸载现有版本&#xff0c;再运行run.bat让其自动安装响应的modules版本。 1、cmd运行windows dos终端。 2、将Fooocus_win64_1-1-1035文件夹备份&#xff0c;rename为Fooocus_win64_1-1-1035backup文…