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

news2024/11/16 8:21:52

冒泡算法

        依次比较两个相邻的子元素,如果他们的顺序错误就把他们交换过来,重复地进行此过程直到没有相邻元素需要交换,即完成整个冒泡,时间复杂度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大常见排序算法,如有问题请指教写。

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

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

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

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

相关文章

MATLAB中circshift函数转化为C语言

背景 有项目算法使用matlab中circshift函数进行运算&#xff0c;这里需要将转化为C语言&#xff0c;从而模拟算法运行&#xff0c;将算法移植到qt。 MATLAB中circshift简单介绍 circshift是循环移位函数。可以使用于数组和矩阵元素的循环移位。 当A是数组 Bcircshift(A,p);如果…

大模型时代,如何以 SDS 助力 AI 发展?

本文根据 XSKY星辰天合产品总监赵琳在 2023 闪存峰会上的演讲。该峰会主题为“芯存储 AI 未来”&#xff0c;由 DOIT 传媒主办、杭州华澜微电子股份有限公司协办&#xff0c;于 8 月 29-30 日在杭州召开。 星辰天合产品总监赵琳在 2023 闪存峰会上发表演讲 人工智能已经从经典…

aac音频格式怎么转mp3?这种转换方法很简单

AAC格式和MP3格式都是数字音频格式&#xff0c;但它们使用不同的音频压缩算法。虽然AAC格式在音质和文件大小方面表现出色&#xff0c;但是不是所有的设备都支持该格式。相比之下&#xff0c;MP3格式的兼容性更好&#xff0c;可以在各种设备和操作系统上播放&#xff0c;因此转…

深入浅出:手把手教你实现单链表

一、什么是链表 链表是一种链状数据结构。简单来说&#xff0c;要存储的数据在内存中分别独立存放&#xff0c;它们之间通过某种方式相互关联。 如果我们使用C语言来实现链表&#xff0c;需要声明一个结构体作为链表的结点&#xff0c;结点之间使用指针关联。 二、单向链表的结…

软件测试(黑盒测试、白盒测试、灰盒测试)

软件测试方法大类上分为黑盒测试、白盒测试和灰盒测试三种 一、黑盒测试 黑盒测试通俗来说即不知道代码是怎么写的。具体实现逻辑&#xff0c;基于代码输入有哪些应该输出什么进行测试的方法。其方法有&#xff1a;基于直觉和经验的方法&#xff08;IEBT&#xff09;、基于需…

b站手机缓存文件转MP4

b站缓存的文件 音频、视频、弹幕是分开的 这里我只用到了音频和视频所以只介绍这一部分 b站的缓存视频文件和路径结构如下 默认缓存路径 内部存储\Android\data\tv.danmaku.bilil\download\89720189 文件夹结构 文件夹 c_738583 这是单个视频的缓存文件夹 进入c_738583文件夹…

isEmpty 和 isBlank 的区别

干了3年java&#xff0c;代码中 isEmpty 和 isBlank 的区别 都不知道&#xff0c;一顿瞎用。也许你两个都不知道&#xff0c;也许你除了isEmpty/isNotEmpty/isNotBlank/isBlank外&#xff0c;并不知道还有isAnyEmpty/isNoneEmpty/isAnyBlank/isNoneBlank的存在, come on ,让我们…

【黑马头条之项目部署_持续集成Jenkins】

本笔记内容为黑马头条项目的项目部署_持续集成部分 目录 一、内容介绍 1、什么是持续集成 2、持续集成的好处 3、今日内容 二、软件开发模式 1、软件开发生命周期 2、软件开发瀑布模型 3、软件的敏捷开发 三、Jenkins安装配置 1、Jenkins介绍 2、Jenkins环境搭建 …

XP-CLR分析学习笔记

XP-CLR分析笔记 生物信息学和遗传学是相互交织的两个学科&#xff0c;共同致力于解读生物体内复杂的基因信息。这些信息有助于理解物种的演化&#xff0c;疾病的起因&#xff0c;以及个体差异等方面。 XP-CLR&#xff08;Cross Population Composite Likelihood Ratio&#xff…

《Linux从练气到飞升》No.19 进程等待

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

【python爬虫】6.爬虫实操(带参数请求数据)

文章目录 前言项目&#xff1a;狂热粉丝分析过程什么是带参数请求数据如何带参数请求数据 代码实现被隐藏的歌曲清单什么是Request Headers如何添加Request Headers 复习 前言 先来复习一下上一关的主要知识吧&#xff0c;先热个身。 Network能够记录浏览器的所有请求。我们最…

钡铼技术BL120PN Profinet和Profibus DP转Modbus网关介绍

​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; BL120PN是一款Profinet、Profibus D转Modbus网关。 BL120PN下行采集支持&#xff1a;Profinet、Profibus D。 BL120PN上行支持&#xff1a;Modbus RTU、Modbus TCP协议。 BL120PN采…

实现微信H5的方法!(非官方产品)

实现微信H5的方法&#xff01;&#xff08;非官方产品&#xff09; WAP支付也叫H5支付&#xff0c;均指的是手机页面支付&#xff0c; 微信称之为H5支付&#xff0c;支付宝则称为WAP支付。 那H5也叫WAP支付是可以应用手机页面支付和APP支付的&#xff0c; 不限制包名可跨平台调…

元素周期表-背诵元素周期表更简单

元素周期表是一款极其炫酷、简约的记忆和查看周期表元素的软件。 【软件特点】&#xff1a; ●有趣谐音速记&#xff1a;软 件内有按周期、化合价、元素符号分类使用谐音速记的小技巧。 ●3D元素周期表&#xff1a;用户可以选择按表面、球体、螺旋、网格来3D炫酷的展示元素周期…

【技术】视频云存储/安防监控视频/智能分析网关V3裸土未覆盖/苫盖算法详解

随着经济的发展和建筑工地的增多&#xff0c;对于土堆的裸露情况实时监测和管理变得尤为重要。为了解决这一问题&#xff0c;TSINGSEEE青犀AI智能分析网关V3的裸土未苫盖算法就能很好地解决。 AI算法模型可以实时识别路面/建筑工地中的土堆是否裸露&#xff0c;将工地、道路等…

煤矿监管电子封条算法

煤矿监管电子封条算法基于yolov5网络模型深度学习框架&#xff0c;先进技术的创新举措&#xff0c;煤矿监管电子封条算法通过在现场运料运人井口、回风井口、车辆出入口等关键位置进行人员进出、人数变化和设备开停等情况的识别和分析。YOLO检测速度非常快。标准版本的YOLO可以…

特斯拉新款Model3曝光:搭载HW3.5硬件,雷达已移除,今日上市?

根据最新的消息&#xff0c;特斯拉新款 Model 3 将有一些令人瞩目的变化和改进。首先&#xff0c;前脸的设计仍然采用封闭式造型&#xff0c;但新车的前保险杠进行了微调&#xff0c;并且换装了全新款式的大灯。 这些改变使新款 Model 3 在外观上与现款车型有了明显的差异&…

【数据结构回顾】

数据结构回顾 一、单链表二、单循环链表 一、单链表 #include <stdio.h> #include <stdlib.h>typedef struct Node {int data;Node *next; }Node;Node* initList() {Node *list (Node*)malloc(sizeof(Node));list->data 0;list->next NULL;return list; }…

基于pytorch LSTM 的股票预测

学习记录于《PyTorch深度学习项目实战100例》 https://weibaohang.blog.csdn.net/article/details/127365867?ydrefereraHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ3MjU2MTYyL2NhdGVnb3J5XzEyMDM2MTg5Lmh0bWw%2Fc3BtPTEwMDEuMjAxNC4zMDAxLjU0ODI%3D 1.tushare Tushare是一个免费、…

C++项目:网络版本在线五子棋对战

目录 1.项目介绍 2.开发环境 3.核心技术 4. 环境搭建 5.websocketpp 5.1原理解析 5.2报文格式 5.3websocketpp常用接口介绍 5.4websocket服务器 6.JsonCpp使用 6.1Json数据格式 6.2JsonCpp介绍 7.MySQL API 7.1MySQL API介绍 7.2MySQL API使用 7.3实现增删改查…