2

Bubble Sort

two-tier cycle
Outer loop: traverse the part to be sorted in the array
Inner loop: compare the size of the current element with the element behind it, and exchange the position if the order is reversed; compare the size of the next element with the element after it, and exchange the position if the order is reversed... until the end

The sorting process of the inner loop is a bubbling process, which is very vivid. When the inner sorting is completed, the last element is the sorted element, just like bubbles emerging from the water.

数组:[5,1,3,4,2]

外层遍历,遍历数组待排序部分:
当前待排序部分是整个数组:
[【5,1,3,4,2】]
    内层遍历,当前元素和后面的元素比较大小,如果逆序就交换位置
    [(5,1),3,4,2] => [(1,5),3,4,2]
    [1,(5,3),4,2] => [1,(3,5),4,2]
    [1,3,(5,4),2] => [1,3,(4,5),2]
    [1,3,4,(5,2)] => [1,3,4,(2,5)]
5已排好序,5之前的部分是待排序部分

当前待排序部分:
[【1,3,4,2】,/5/]
    内层遍历,当前元素和后面的元素比较大小,如果逆序就交换位置
    [(1,3),4,2,/5/] => [(1,3),4,2,/5/]
    [1,(3,4),2,/5/] => [1,(3,4),2,/5/]
    [1,3,(4,2),/5/] => [1,3,(2,4),/5/]
4已排好序,4之前的部分是待排序部分

...以此类推

time complexity:

Worst case:
Bubble sort has a total of (n-1) cycles, and each cycle requires the current n-1 comparisons
So the total number of comparisons is: (n-1) + (n-2) + (n-3) + … + 1 = n*(n-1)/2;
So the time complexity of bubble sort is O(n2)

Best case: The array to be sorted is in positive order, and the sorting can be completed in one round of scanning. At this time, the time complexity is only the time complexity of the comparison, which is O(n)

Average case: O(n2)

Space complexity:
It is the memory space occupied by the temporary variable when the elements are exchanged. The optimal space complexity is that the starting element order has been arranged, then the space complexity is 0, and the worst space complexity is that the starting elements are sorted in reverse order, then The space complexity is O(n), and the average space complexity is O(1)

def bubble_sort(nums):
    n = len(nums)
    
    for i in range(n):
        # 外层每结束一次循环,就有1个元素已排序
        # i=0 没有已排序元素;i=1 1个已排序元素;i=2 2个已排序元素...
        # 所以内层循环次数 = 元素个数-i
        # 同时内层循环最后一次遍历,只需要遍历到倒数第二个元素,和倒数第一元素比较即可,所以内层循环次数 = 元素个数-i-1
        for j in range(n-i-1):
            if nums[j] > nums[j+1]:
                nums[j],nums[j+1]=nums[j+1],nums[j]
    return nums

quick sort

Quick sort has a feeling of dichotomy + double pointer + recursion
Select a reference value, set two pointers left and right, left is responsible for the part smaller than the reference value, right is responsible for the part larger than the reference value, and divide the array into two parts smaller than this value and greater than or equal to this value
Then treat these two parts as a new array and repeat the above steps
until the final two parts have only 1 element

specific process:

数组= [4,3,5,7,6,1,2,4]

设置一个基准值,一般选择数组第一个元素即可
基准值:4
设置两个指针,left在头,right在尾

基准值:4
[4(l),3,5,7,6,1,2,4(r)]
因为left指向的元素就是基准值本身,所以我们先去比较right指向的元素
right也指向4,>=基准值,属于right的范围,不做改变,right向前移动

基准值:4
[4(l),3,5,7,6,1,2(r),4]
right指向2,<基准值,属于left的范围,将2赋值给left指针,left向后移动

基准值:4
[2,3(l),5,7,6,1,2(r),4]
left指向3,<基准值,属于left的范围,不做改变,left向后移动

基准值:4
[2,3,5(l),7,6,1,2(r),4]
left指向5,>=基准值,属于right的范围,将5赋值给right指针,right向前移动

基准值:4
[2,3,5(l),7,6,1(r),5,4]
right指向1,<基准值,属于left的范围,将1赋值给left指针,left向后移动

基准值:4
[2,3,1,7(l),6,1(r),5,4]
left指向7,>=基准值,属于right的范围,将7赋值给right指针,right向前移动


基准值:4
[2,3,1,7(l),6(r),7,5,4]
right指向6,>=基准值,属于right的范围,不做改变,right向前移动

基准值:4
[2,3,1,7(l,r),6,7,5,4]
此时left和right相遇,将该值改为基准值

基准值:4
[2,3,1,4(l,r),6,7,5,4]
此时指针的左半部分[2,3,1]都是小于基准值4的
指针的右半部分[6,7,5,4]都是大于等于基准值4的
这两个部分重新选额各自的基准值,重复上述快排步骤,直到递归结束
最终就将整个数组完成排序

time complexity:

  • Best case: O(nlogn)
    In the best case, each time the reference value is in the middle of the array and the array is just divided equally, after logn divisions, the left and right parts are divided
    Each division involves n elements
    So the total time is O(n)*O(logn)=O(nlogn)
  • Worst case: O(n^2)
    In the worst case, each time the reference value is at the beginning of the array, and the array is completely positive, then it needs to be divided n times to separate the left and right parts.
    Each division involves n elements
    So the total time is O(n)*O(n)=O(n^2)
  • Average: O(nlogn)

Space complexity:
In the optimal case, the space complexity is: O(logn); the case where the array is equally divided every time
Worst case space complexity: O(n); degenerates to bubble sort

def quickSort(nums,left,right):
    # 递归结束条件:子数组长度被分割到只有1个元素是,此时left和right指针指向同一个元素,left和right的值相等
    if left>=right:
        return
    
    # 递归执行快排操作
    
    # 先对传递进来的数组,执行选取基准值、排出较小、较大两部分
    mid = partition(nums,left,right)
    
    # 再分别对 较小部分、较大部分,再次递归执行快排
    quickSort(nums,left,mid-1)
    quickSort(nums,mid+1,right)
    
    return nums
    

def partition(nums,left,right):
    # 设置数组第一个元素作为基准值
    pivot = nums[left]
    
    # 遍历元素,移动left、right指针,
    while left < right:
        # 此处必须先判断right部分,因为第一个left是基准值,相当于一个空位
        # 如果先判断left部分的话,left先移动,那么基准值的那个空位就永远空着了
        # 在left和right相遇之前,right指向的元素>=基准值,就是正常的,只向前移动right指针
        while left < right and nums[right]>=pivot:
            right-=1
        # 否则,nums[right]<pivot,则将right的值赋值给left
        nums[left] = nums[right]
        
        # 在left和right相遇之前,left指向的元素<=基准值,就是正常的,只向后移动left指针
        while left < right and nums[left]<=pivot:
            left+=1
        # 否则,nums[left]>pivot,则将left的值赋值给right
        nums[right] = nums[left]
        
    # 最后,当left=right时,left和right相遇,排序结束
    # 将基准值放在该位置上
    nums[left] = pivot
    # 返回基准值的索引
    return left

selection sort

Divide the array into sorted part and to-be-sorted part
Iterate over the to-be-sorted part of the array multiple times
Each time it is traversed, the minimum value of the part to be sorted is selected, and it exchanges positions with the first element of the part to be sorted, becoming the sorted part

数组:[5,1,3,4,2]

开始时,整个数组都是待排序部分
[【5,1,3,4,2】]

待排序部分最小值为1,和待排序部分第一个元素5交换位置,1变成已排序
[1,【5,3,4,2】]

待排序部分最小值为2,和待排序部分第一个元素5交换位置,2变成已排序
[1,2,【3,4,5】]

待排序部分最小值为3,和待排序部分第一个元素3交换位置,3变成已排序
[1,2,3,【4,5】]

待排序部分最小值为4,和待排序部分第一个元素4交换位置,4变成已排序
[1,2,3,4,【5】]

此时待排序部分只有一个元素5,不需要排序了,就完成了全部排序
[1,2,3,4,5]

Time complexity: O(n^2)
The first inner loop compares N - 1 times, then N-2 times, N-3 times, ..., and the last inner loop compares 1 time.
The total number of comparisons is (N - 1) + (N - 2) + ... + 1, and the arithmetic sequence sum is obtained to obtain (N - 1 + 1)* N / 2 = N^2 / 2.
The time complexity of discarding the highest term coefficient is O(N^2).

def selectionSort(nums):
    n = len(nums)
    
    for i in range(n):
        # 设置一个指针,保存待排序部分最小值的索引
        min_index = i
        # 遍历待排序部分,从i开始,i之前的是已排序部分
        for j in range(i,n):
            # 更新最小值的索引
            if nums[j]<nums[min_index]:
                min_index = j
        # 将待排序部分的第一个元素,和最小值元素,交换位置
        # 相当于将最小值放到已排序部分,待排序部分向后缩
        nums[i],nums[min_index] = nums[min_index],nums[i]
    return nums

Insertion sort

Divide the array into sorted part and to-be-sorted part
Start with the first element of the array as the sorted part
Traverse each element of the part to be sorted, compare with the elements of the sorted part, and insert into the correct position in the sorted part

数组:[5,1,3,4,2]

开始时,第一个元素5当作已排序部分,遍历待排序部分的元素
[/5/,(1),3,4,2]

待排序的1,小于已排序的5,将1放在5前面
[/1,5/,(3),4,2]

待排序的3,小于已排序的5,将3放在5前面
[/1,3,5/,(4),2]
待排序的3,大于已排序的1,将3放在1后面,即不改变位置
[/1,3,5/,(4),2]

待排序的4,小于已排序的5,将4放在5前面
[/1,3,4,5/,(2)]
待排序的4,大于已排序的3,将4放在3后面,即不改变位置
[/1,3,4,5/,(2)]

待排序的2,小于已排序的5,将2放在5前面
[/1,3,4,2,5/]
待排序的2,小于已排序的4,将2放在4前面
[/1,3,2,4,5/]
待排序的2,小于已排序的3,将2放在3前面
[/1,2,3,4,5/]
待排序的2,大于已排序的1,将2放在1后面,即不改变位置
[/1,2,3,4,5/]

排序完毕

time complexity:

  • Best case: [1,2,3,4,5], traverse n-1 times, move 0 times, time complexity O(n)
  • Worst case: [5,4,3,2,1], traverse n-1 times, move 1+2+...+n-1 times, time complexity O(n^2)
  • Average: O(N^2)
def insertionSort(nums):
    n = len(nums)
    
    # 将数组第一个1元素当作已排序,之后的作为待排序
    # 遍历待排序部分
    for i in range(1,n):
        # 用一个变量保存当前待排序元素,为之后插入操作,腾出插入空间
        tmp = nums[i]
        # i之前是已排序部分,遍历已排序部分,从后向前遍历
        for j in range(i-1,0,-1):
            # 如果 当前已排序元素nums[j] 大于 待排序元素nums[i]
            # 则 已排序元素向后移,最后一个已排序元素后面就是待排序元素
            # 之前已经用变量保存了待排序元素,所以不用担心待排序元素会丢失
            if nums[j]>nums[i]:
                nums[j+1]=nums[j]
            # 如果 当前已排序元素nums[j] 小于 待排序元素nums[i]
            # 则j+1是空位,将待排序元素插入j+1位置
            else:
                nums[j+1] = nums[i]
                break
    return nums

merge sort

First, the array is continuously divided into two parts until the sub-array has only one element and cannot be further divided.

Then start to sort up the merged subarrays, use two pointers to point to the two subarrays, compare the size of the elements at the pointer positions, add the smaller element to the array first, and move the pointer to continue the comparison.

数组:[8,4,5,7,1,3,6,2]

先向下递归二分数组,直到子数组不可再分
 [8,4,5,7]     [1,3,6,2]
[8,4] [5,7]   [1,3] [6,2]
[8][4][5][7]  [1][3][6][2]

开始向上递归,通过两个指针比较元素大小,排序合并每层子数组
[8][4][5][7] [1][3][6][2]
[4,8] [5,7]  [1,3] [2,6]
 [4,5,7,8]    [1,2,3,6]
    [1,2,3,4,5,6,7,8]

指针排序过程:
例如
[4,8] [5,7]

两个子序列开头设置指针
[4(i),8] [5(j),7]

比较指针元素大小,较小的放入结果中,并向前移动该指针
[ ,8(i)] [5(j),7]
res=[4]

重复上述步骤
[ ,8(i)] [,7(j)]
res=[4,5]

[ ,8(i)] [ , ]
res=[4,5,7]

[ ,] [ , ]
res=[4,5,7,8]

Time complexity: O(nlogn)
For a sequence of length n, its sorting time is equal to the sorting time of 2 subsequences of length n/2 after it is divided, plus their merging time.
Assuming a sequence of length n, the sorting time is T(n), then the subsequence of length n/2, the sorting time is T(n/2), two subsequences of n/2 are merged, and n operations are required elements, so the merge time of T(n) is n
So, T(n)=2*T(n/2)+n

And so on,
For a subsequence of length n/2, its sorting time T(n/2)=2*T(n/4)+n/2
For a subsequence of length n/4, its sorting time T(n/4)=2*T(n/8)+n/4
...
Bring T(n/4) into T(n/2), and then bring T(n/2) into T(n)
Get each layer T(n)
T(n)=2*T(n/2)+n
T(n)=4*T(n/4)+2n
T(n)=8*T(n/8)+3n
...
The subsequence length of the last layer is 1, so the sorting time of the last layer is T(1), and using T(1) to represent T(n) is
logn means: an array of length n can be indivisible by bisection logn times
T(n)=n*T(1)+(logn)*n
Because T(1)=0, so T(n)=nlogn

def mergeSort(nums):
    n = len(nums)
    # 递归二分数组结束条件
    # 当递归二分数组,分到不可分时,返回数组
    if n==1:
        return nums
    # 找到数组中间位置
    mid = n//2
    # 递归二分数组
    left = mergeSort(nums[:mid])
    right = mergeSort(nums[mid:])
    
    # 合并排序部分
    tmp = []
    # 当 左右数组 都不为空时,比较左右的第1个元素,较小的放入结果
    while left and right:
        if left[0]<right[0]:
            tmp.append(left.pop(0))
        else:
            tmp.append(right.pop(0))
    # 当 右数组为空时,左数组的元素从第1个开始,挨个放入结果
    while left:
        tmp.append(left.pop(0))
    # 当 左数组为空时,右数组的元素从第1个开始,挨个放入结果
    while right:
        tmp.append(right.pop(0))
    # 返回该层的合并排序结果
    return tmp

Ethan
140 声望11 粉丝

水平较低,只是记录,谨慎参阅


引用和评论

0 条评论