Leetcode刷题09-排序

排序

基础知识

1.简介

排序:将一组无序的记录序列按照某种逻辑顺序重新排序,调整为有序的记录序列的过程。

2.排序算法分类:

由于待排序的记录数量不同,使得排序过程中涉及的存储器不同,可将排序算法分为两大类:

  • 内部排序算法:当参加排序的数据量不大时,在排序过程中将全部记录存放在内存中处理
  • 外部排序算法:当参加排序的数据量较大时,以致于内存不足与一次存放全部记录,在排序过程中需要通过内存与外村之间的数据交换达到排序目的。

对于具有多个相同值的记录序列来说,如果采用的排序算法使得排序前后拥有相同值记录的相对位置是否有改变可分为:

  • 稳定性排序算法:对于值相同的两个元素,排序前后的的先后次序不变
  • 非稳定性排序算法:对于值相同的两个元素,排序前后的先后次序改变

根据记录在存储介质上的组织方式划分排序算法的分类可分为:

  • 顺序存储结构排序算法:记录之间的逻辑顺序通过物理地址的先后映射,在排序过程中需要移动记录的位置
  • 链式存储结构排序算法:文件中的一个记录对应着链表中的一个链节点,记录之间的逻辑顺序是通过指针来反应,因而排序过程中不必移动记录,只需修改相应指针的指向

3.常见排序算法

常见的排序算法主要有十种:冒泡排序算法、选择排序算法、插入排序算法、希尔排序算法、快速排序算法、堆排序算法、计数排序算法、桶排序算法、基数排序算法。

按照时间复杂度划分:

  • O(n^2):冒泡排序、选择排序、插入排序
  • O(n * logn):希尔排序、归并排序、快速排序、堆排序
  • O(n):计数排序、桶排序、基数排序

4.排序算法基本思路及实现

冒泡排序基本思想:通过相邻元素之间的比较与交换,使值较小的元素逐步从后面移到前面,值较大的元素从前面移到后面,就像水底的气泡一样向上冒,是稳定性排序算法

def bubbleSort(self, arr):
	for i in range(len(arr)):
        for j in range(len(arr) - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
    return arr

选择排序基本思想:每一趟排序中,从剩余未排序元素中选择一个最小的元素,与未排好序的元素最前面一个交换位置,是非稳定性排序算法

def selectionSort(arr):
    for i in range(len(arr) - 1):
        min_pos = i
        for j in range(i + 1, len(arr)):
            if arr[min_pos] > min[j]:
                min_pos = j
        if i != min_pos:
            arr[i], arr[min_pos] = arr[min_pos], arr[i]
   	return arr

插入排序基本思想:每一趟排序中,将剩余未排序元素中第一个元素,插入到排序元素中的合适位置上,是稳定性排序算法

def insertionSort(arr):
    for i in range(1, len(arr)):
        temp = arr[i]
        j = i
        while j > 0 and arr[j - 1] > temp:
            arr[j] = arr[j - 1]
            j -= 1
       	arr[j] = temp
    return arr

希尔排序基本思想:将整个序列按照一定间隔取值划分为若干个子序列,每个子序列使用插入排序,直到最后一轮排序间隔未1,对整个序列进行插入排序,为非稳定性排序算法

def shellSort(arr):
    size = len(arr)
    gap = size // 2
    while gap > 0:
        for i in range(gap, size):
           	temp = arr[i]
            j = i
            while j >= gap and arr[j - gap] > temp:
				arr[j] = arr[j - gap]
                j -= gap
            arr[j] = temp
        gap = gap // 2
    return arr

归并排序基本思想:采用经典的分治策略,先递归将当前序列分为两半,然后将有序序列合并成一个有序序列,是稳定性排序算法

def merge(left_arr, right_arr):
    arr = []
    while left_arr and right_arr:
        if left_arr[0] <= right_arr[0]:
            arr.append(left_arr.pop(0))
        else:
            arr.append(right_arr.pop(0))
   	while left_arr:
        arr.apend(left_arr.pop(0))
        
    while right_arr:
        arr.apend(right_arr.pop(0))
        
   	return arr
    
    
def mergeSort(arr):
    size = len(arr)
    if size < 2:
        return arr
    mid = len(arr) // 2
    left_arr, right_arr = arr[0: mid], arr[mid:]
    return merge(mergeSort(left_arr), mergeSort(right_arr))

快速排序基本思想:通过一趟排序将无序序列分为独立的两个序列,第一个序列的值均比第二个序列的值小。然后递归两个子序列,以达到整个序列有序,非稳定性排序算法

def randomPartition(arr, low, high):
    i = random.randint(low, high)
    arr[i], arr[high] = arr[high], arr[i]
    return partition(arr, low, high)

def partition(arr, low, high):
    i = low - 1
    pivot = arr[high]
    
    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
   	arr[i + 1], arr[high] = arr[high], arr[i + 1]
    return i + 1

def quickSort(arr, low, high):
    if low < high:
        pi = randomPartition(arr, low, high)
        quickSort(arr, low, pi - 1)
        quickSort(arr, pi + 1, high)

堆排序基本思想:借用堆结构设计的排序算法,将数组转化为大顶堆,重复从大顶堆中取出数值最大的节点,并让剩余的堆维持大顶堆性质,非稳定性排序

def heapify(arr, index, end):
    left = index * 2 + 1
    right = left + 1
    while left <= end:
        max_index = index
        if arr[left] > arr[max_index]:
            max_index = left
        if right <= end and arr[right] > arr[max_index]:
            max_index = right
        if index == max_index:
            break
        arr[index], arr[max_index] = arr[max_index], arr[index]
        index = max_index
        left = index * 2 + 1
        right = left + 1
        
def buildMaxHeap(arr):
    size = len(arr)
    for i in range((size - 2) // 2, -1, -1):
        heapify(arr, i, size - 1)
    return arr


def maxHeapSort(arr):
    buildMaxHeap(arr)
    size = len(arr)
    for i in range(size):
        arr[0], arr[size - i - 1] = arr[size - i - 1], arr[0]
        heapify(arr, 0, size - i - 2)
    return arr

计数排序基本思想:使用额外的数组counts,其中counts[i]保存arr中值为i的个数,然后根据数组counts将arr中元素排到正确的位置,是稳定排序算法

def countingSort(arr):
    arr_min, arr_max = min(arr), max(arr)
    size = arr_max - arr_min + 1
    counts = [0 for _ in range(size)]
    
    for num in arr:
        counts[num - arr_min] += 1
    for j in range(1, size):
        counts[j] += counts[j - 1]
        
    res = [0 for _ in range(len(arr))]
    for i in range(len(arr) - 1, -1, -1):
        res[counts[arr[i] - arr_min] - 1] = arr[i]
        counts[arr[i] - arr_min] -= 1
        
   	return res

桶排序基本思想:将未排序的数组分成若干个桶(分成n个大小相同的区间),每个桶中的元素再进行单独排序(使用插入、归并、快排等),是稳定性排序算法

def insertionSort(arr):
    for i in range(1, len(arr)):
        temp = arr[i]
        j = i
        while j > 0 and arr[j - 1] > temp:
            arr[j] = arr[j - 1]
            j -= 1
        arr[j] = temp
    return arr


def bucketSort(arr, bucket_size = 5):
    arr_min, arr_max = min(arr), max(arr)
    bucket_count = (arr_max - arr_min) // bucket_size + 1
    buckets = [[] for _ in range(bucket_count)]
    
    for num in arr:
        buckets[(num - arr_min) // bucket_size].append(num)
        
    res = []
    for bucket in buckets:
        insertionSort(bucket)
        res.extend(bucket)
    return res

基数排序基本思想:将整数按位数切割成不同的数字,然后按每个位数分别比较进行排序,是稳定性排序算法

def radixSort(arr):
    size = len(str(max(arr)))
    
    for i in range(size):
        buckets = [[] for _ in range(10)]
        for num in arr:
            buckets[num // (10 ** i) % 10].append(num)
        arr.clear()
        for bucket in buckets:
            for num in bucket:
                arr.append(num)
   	return arr

题目解析

合并两个有序数组

1.题目描述

题目链接

Leetcode刷题09-排序_第1张图片

2.解析思路及代码

使用两个指针指向两个数组的逻辑长度尾部,再用一个指针指向第一个数组的真实长度尾部,然后把较大元素放到第一个数组的尾部,然后相应指针变化即可,最后如果第二个数组不为空则将第二个数组所有值存到第一个数组中。

	public void merge(int[] nums1, int m, int[] nums2, int n) {
        int index1 = m - 1, index2 = n - 1, index = m + n - 1;
        
        while (index1 >= 0 && index2 >= 0) {
            if (nums1[index1] <= nums2[index2]) {
                nums1[index] = nums2[index2];
                index2 --;
            } else {
                nums1[index] = nums1[index1];
                index1 -- ;
            }
            index -- ;
        }
        
        while (index2 >= 0) {
            nums1[index] = nums2[index2];
            index -- ;
            index2 -- ;
        }
    }
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        index = m + n - 1
        m -= 1
        n -= 1
        while m >= 0 and n >= 0:
            if nums1[m] <= nums2[n]:
                nums1[index] = nums2[n]
                n -= 1
            else:
                nums1[index] = nums1[m]
                m -= 1
            index -= 1
        while n >= 0:
            nums1[index] = nums2[n]
            index -= 1
            n -= 1
            

排序数组

1.题目描述

题目链接

Leetcode刷题09-排序_第2张图片

2.解题思路及代码

使用以上讲解的任意算法即可。

	public int[] sortArray(int[] nums) {
        Arrays.sort(nums);
        return nums;
    }
class Solution:
    def randomized_partition(self, nums, l, r):
        pivot = random.randint(l, r)
        nums[pivot], nums[r] = nums[r], nums[pivot]
        i = l - 1
        for j in range(l, r):
            if nums[j] < nums[r]:
                i += 1
                nums[j], nums[i] = nums[i], nums[j]
        i += 1
        nums[i], nums[r] = nums[r], nums[i]
        return i

    def randomized_quicksort(self, nums, l, r):
        if r - l <= 0:
            return
        mid = self.randomized_partition(nums, l, r)
        self.randomized_quicksort(nums, l, mid - 1)
        self.randomized_quicksort(nums, mid + 1, r)

    def sortArray(self, nums: List[int]) -> List[int]:
        self.randomized_quicksort(nums, 0, len(nums) - 1)
        return nums
        

有序数组的平方

1.题目描述

题目链接

Leetcode刷题09-排序_第3张图片

2.解题思路及代码

  • 双指针分别指向数组两侧,然后判断平方值的大小
	public int[] sortedSquares(int[] nums) {
        int l = 0, r = nums.length - 1;
        int[] res = new int[nums.length];
        int index = nums.length - 1;
        while (l <= r) {
            if (nums[l] * nums[l] >= nums[r] * nums[r]) {
                res[index] = nums[l] * nums[l];
                l ++ ;
            } else {
                res[index] = nums[r] * nums[r];
                r -- ;
            }
            index -- ;
        }
        return res;
    }
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        l, r = 0, len(nums) - 1
        index = len(nums) - 1
        res = [0] * len(nums)
        while l <= r:
            if nums[l] * nums[l] >= nums[r] * nums[r]:
                res[index] = nums[l] * nums[l]
                l += 1
            else:
                res[index] = nums[r] * nums[r]
                r -= 1
            index -= 1
        return res

数组的相对排序

1.题目描述

题目链接

Leetcode刷题09-排序_第4张图片

2.解题思路及代码

计数排序

class Solution {
    public int[] relativeSortArray(int[] arr1, int[] arr2) {
        int bounds = 0;
        for (int x : arr1) {
            bounds = Math.max(bounds, x);
        }
        
        int[] frequency = new int[bounds + 1];
        
        for (int x : arr1) {
            frequency[x] ++ ;
        }
        int[] ans = new int[arr1.length];
        int index = 0;
        for (int x : arr2) {
            for (int i = 0; i < frequency[x]; i ++ ) {
                ans[index ++ ] = x;
            }
            frequency[x] = 0;
        }
        
        for (int i = 0; i <= bounds; i ++ ) {
            for (int j = 0; j < frequency[i]; j ++ ) {
                ans[index ++ ] = i;
            }
        }
        return ans;
    }
}
class Solution:
    def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
        bounds = max(arr1)
        frequency = [0] * (bounds + 1)
        for x in arr1:
            frequency[x] += 1
        
        ans = list()
        
        for x in arr2:
            ans.extend([x] * frequency[x])
            frequency[x] = 0
        
        for x in range(bounds + 1):
            if frequency[x] > 0:
                ans.extend([x] * frequency[x])
        
        return ans

对链表进行插入排序

1.题目描述

题目链接

Leetcode刷题09-排序_第5张图片

2.解题思路及代码

插入排序的思路,需要注意两点:

  • 防止边界问题,使用带有头结点的链表
  • 在找当前节点需要插入的位置时,正常针对数组的插入排序从i - 1开始找,但是这是链表,需要从头节点开始找。
	public ListNode insertionSortList(ListNode head) {
        if (head == null || head.next == null)  return head;
        
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode lastSorted = head, cur = head.next;
        
        while (cur != null) {
            if (lastSorted.val <= cur.val) {
                lastSorted = lastSorted.next;
            } else {
                ListNode pre = dummyHead;
                while (pre.next.val <= cur.val)    pre = pre.next;
                lastSorted.next = cur.next;
                cur.next = pre.next;
                pre.next = cur;
            }
            
            cur = lastSorted.next;
        }
        return dummyHead.next;
    }
}
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        if not head:
            return head
        
        dummyHead = ListNode(0, head);
        lastSorted, cur = head, head.next
        
        while cur:
            if lastSorted.val <= cur.val:
                lastSorted = lastSorted.next
            else:
                pre = dummyHead
                while pre.next.val <= cur.val: 
                    pre = pre.next
                lastSorted.next = cur.next
                cur.next = pre.next
                pre.next = cur
            cur = lastSorted.next
        
        return dummyHead.next

你可能感兴趣的:(leetcode,算法,职场和发展)