zoukankan      html  css  js  c++  java
  • 归并排序

    归并排序,是创建在归并操作上的一种有效的排序算法。算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。

    按照分治三步法,对归并排序算法介绍如下:
    划分问题:把序列分成元素个数尽量相等的两半。
    递归求解:把两个元素分别排序。
    合并问题:把两个有序表合并成为一个。

    前两部分是很容易完成的,关键在于如何把两个有序表合并成为一个。合并的过程为每次都把两个有序表中最小的元素加以比较,删除其中的较小元素并加入合并后的新表中。由于需要一个新表来存放结果,所以附加空间为 n 。

    习题

    题目一:2020.09.04 [009] 数组中的逆序对

    1.题目描述

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

    示例 1:
    输入: [7,5,6,4]
    输出: 5
    限制:

    0 <= 数组长度 <= 50000

    2.解题思路

    这道题给定了数组长度的范围,这表示对时间还是有要求的,用暴力求解肯定行不通,因此这里使用归并排序。
    image.png
    问题的关键在于合并这一步,假设当前有两个有序数组,这里需要一个辅助数组。需要三个指针 i , j 和 k,分别指向辅助数组中的两个有序数组以及合并后的数组。
    image.png
    在合并时比较辅助数组中两个有序表,比较指针i,j当前指向的最小值。将两者之中最小的放入原始数组中,这里分成两种情况:
    tmp[i] <= tmp[j]:由于tmp[i]之前并无比它还要大的数,因此对逆序数没有影响。此时只需要令nums[k] = tmp[i],并是i,k指针分别加一即可。
    注意这里使用小于等于号的目的是保持归并排序的稳定性(排序算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] = r[j],且 r[i] 在 r[j] 之前,而在排序后的序列中,r[i] 仍在 r[j] 之前,则称这种排序算法是稳定的;否则称为不稳定的。)
    tmp[i] > tmp[j]:此时 tmp[j] 前面的所有数字都比它大,因此逆序数会增加,增加的数值等于tmp[j] 前面的数字个数(即 mid - i + 1)。

    class Solution:
        def merge_sort(self, nums, tmp, left, right):
            if(left >= right):
                return 0
            mid = (left + right) // 2
            sum = self.merge_sort(nums, tmp, left, mid) + self.merge_sort(nums, tmp, mid + 1, right)
            i, j, k = left, mid + 1, left
            tmp[left:right + 1] = nums[left:right + 1]
            
            while(i <= mid and j <= right):
                if(tmp[i] <= tmp[j]):
                    nums[k] = tmp[i]
                    i += 1
                    k += 1
                elif(tmp[i] > tmp[j]):
                    nums[k] = tmp[j]
                    sum += mid - i + 1
                    k += 1
                    j += 1
            while(i <= mid):
                nums[k] = tmp[i]
                k += 1
                i += 1
            while(j <= right):
                nums[k] = tmp[j]
                k += 1
                j += 1
            return sum
    
        def reversePairs(self, nums: List[int]) -> int:
            length = len(nums)
            tmp = [0] * length
            return self.merge_sort(nums, tmp, 0, length - 1)
    

    题目二:2020.09.05 [010] 合并两个排序的链表

    1.题目描述

    输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
    示例1:
    输入:1->2->4, 1->3->4
    输出:1->1->2->3->4->4

    限制:
    0 <= 链表长度 <= 1000

    2.解题思路

    利用两个列表有序的性质,这道题只涉及到归并排序中的合并的那一步。

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
            cur = dum = ListNode(0)
            while(l1 and l2):
                if(l1.val <= l2.val):
                    cur.next, l1 = ListNode(l1.val), l1.next
                else:
                    cur.next, l2 = ListNode(l2.val), l2.next
                cur = cur.next
    
            cur.next = l1 if l1 else l2
            
            return dum.next
    

    题目三:2020.09.23 [011] 字符串的排列

    1.题目描述

    输入一个字符串,打印出该字符串中字符的所有排列。
    你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

    示例:
    输入:s = "abc"
    输出:["abc","acb","bac","bca","cab","cba"]

    限制:
    1 <= s 的长度 <= 8

    2.解题思路

    一直一个长度为N的字符串s,若s中没有重复字符串,那么这个字符串共有N!中排列方式。这个排列可以由深度优先算法dfs求出,从第0位一直到第 N-1 位开始固定,通过将当前固定位x与[x, N-1]上的字符进行交换来变换当前固定位x上的字符。
    但是当字符串中出现重复字符时,我们就要进行剪枝。用一个集合记录当前位置出现的字符,若刚交换的字符出现在集合中,那么直接跳过,如果没出现过,则交换后进行下一位的固定,继续执行dfs(x+1)。
    image.png

    class Solution:
        def permutation(self, s: str) -> List[str]:
            c, res = list(s), []
            def dfs(x):
                if x == len(c) - 1:
                    res.append(''.join(c))
                    return
                dict = set()
                for i in range(x, len(c)):
                    if c[i] in dict:
                        continue
                    dict.add(c[i])
                    c[x], c[i] = c[i],c[x]
                    dfs(x+1)
                    c[x], c[i] = c[i],c[x]
            dfs(0)
            return res
    
  • 相关阅读:
    熬夜的朋友看一看 [转]
    配置ubuntu
    C++string类常用函数 (转)
    ArcGIS Engine栅格数据使用总结 (转)
    fstream的用法+代码
    [转] 英语飙升的好方法
    MFC 非模态对话框(转)
    十九个国内外主流的三维GIS软件(转)
    Google C++ 风格指南 中文版
    std::set用法(转)
  • 原文地址:https://www.cnblogs.com/idella/p/13717991.html
Copyright © 2011-2022 走看看