Problem Set 4

Problem Set 4.1

Problem 4.1.1

首先判断\(A[1]\)\(A[n]\)是不是峰值,如果是的话就找到了,如果都不是的话就说明数列在开头一段处于单调递增的状态,末尾一段处于单调递减的状态,于是可以知道在\([2,n-1]\)中一定存在峰值。接下来使用二分,设当前在区间\([l,r]\)中存在答案(初始化\(l=1,r=n\)并且这个区间中开头一段单调递增末尾一段单调递减,令\(mid=\lfloor\frac{l+r}{2}\rfloor\),如果\(A[mid]\)是峰值,就找到了,否则是如下三种情况

  • \(A[mid-1]<A[mid]<A[mid+1]\),此时在\([mid,r]\)这段区间中一定存在答案(因为这段区间开头一段单调递增末尾一段单调递减),令\(l=mid\)
  • \(A[mid-1]>A[mid]>A[mid+1]\),此时在\([l,mid]\)这段区间中一定存在答案(因为这段区间开头一段单调递增末尾一段单调递减),令\(r=mid\)
  • \(A[mid-1]>A[mid]<A[mid+1]\),这是令\(l=mid\)\(r=mid\)都行

综上,时间复杂度为\(O(\log n)\)

def find_peak(nums):
    n = len(nums)
    if n == 1:
        return 0
    if nums[0] > nums[1]:
        return 0
    if nums[-1] > nums[-2]:
        return n - 1
    left, right = 1, n - 2
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] > nums[mid - 1] and nums[mid] > nums[mid + 1]:
            return mid
        elif nums[mid - 1] < nums[mid] < nums[mid + 1]:
            left = mid + 1
        elif nums[mid - 1] > nums[mid] > nums[mid + 1]:
            right = mid - 1
        else:
            left = mid + 1

Problem 4.1.2

\((1)\)不平衡,反例如下
image
\((2)\)平衡,按照\(k\)归纳(归纳更强的结论:当二叉树满足题述性质的时候,二叉树一定是满二叉树)
\(k=0\)的时候,此时没有节点,显然是一个满二叉树
\(\forall k<c\)的时候树都是满二叉树的,那么当\(k=c\)的时候,设根节点的两个儿子的大小分别为\(2^{k_1}-1,2^{k_2}-1(k_1,k_2∈\mathbb{N})\),则有\(2^k-1=2^{k_1}-1+2^{k_2}-1+1=2^{k_1}+2^{k_2}-1\).根据二进制数的性质,\(2^k\)的二进制表示只有一个\(1\),而如果\(k_1≠k_2\),那么\(2^{k_1}+2^{k_2}\)的二进制表示下有两个\(1\),所以\(k_1=k_2=k-1\).根据数学归纳,两棵子树都是满二叉树,而且两棵子树的大小是相同的,所以两个子树一模一样,在加上根节点可知,整棵树是一个满二叉树,当然也就平衡了
\((3)\)平衡。假设一共有\(n\)个节点,那么根节点的较大子树的大小不会超过\(\frac{n-1}{1+c}\),于是有\(H(n)\leq 1+H(\frac{n-1}{1+c})\leq1+1+H(\frac{n-1}{(1+c)^2})\leq...\),所以节点以指数级别缩小,于是可以知道平衡(注意\(\Theta(\log_p n)=\Theta(\frac{\log_q n}{\log_q p})=\Theta(\log_q n)\)

4.1.3

利用数学归纳法

  • \(T\)\(RB_h\)的情况:
    • \(h=0\)的时候,显然都满足
    • \(\forall h<c\)的时候都满足,则当\(h=c\)的时候,考虑根的两个儿子的情况
      • 两个儿子都是\(RB_{h-1}\)
        • \(T\)至少有\(2\times(2^{h-1}-1)+1=2^h-1\)个内部黑节点
        • \(T\)至多有\(2\times(4^{h-1}-1)+1\lt 4^h-1\)个内部节点
        • 要证明性质三,只需要证明根节点的深度最多是\(T\)的black depth的两倍。此时根节点的深度是两个儿子节点深度更大者加一,而\(T\)的black depth是任意一个儿子节点的black depth加一,于是不难证明
      • 两个儿子一个是\(RB_{h-1}\),另一个是\(ARB_{h}\)
        • 由于\(ARB_{h}\)的两个儿子都是\(RB_{h-1}\),于是\(T\)至少有\(2^{h-1}-1+2\times(2^{h-1}-1)+1\gt 2^h-1\)个内部黑节点
        • 由于\(ARB_{h}\)的两个儿子都是\(RB_{h-1}\),于是\(T\)至多有\(4^{h-1}-1+2\times(4^{h-1}-1)+2\lt 4^h-1\)个内部节点
        • 要证明性质三,只需要证明根节点的深度最多是\(T\)的black depth的两倍。此时根节点的深度是两个儿子节点深度更大者加一,而\(T\)的black depth是任意一个儿子节点的black depth加一(注意其中一个节点是红色根节点,所以不影响这个结论),于是不难证明
      • 两个儿子都是\(ARB_{h}\)
        • \(T\)至少有\(4\times(2^{h-1}-1)+1\gt2^h-1\)个内部黑节点
        • \(T\)至多有\(4\times(4^{h-1}-1)+3=4^h-1\)个内部节点
        • 要证明性质三,只需要证明根节点的深度最多是\(T\)的black depth的两倍。此时根节点的深度是两个儿子节点深度更大者加一,而\(T\)的black depth是任意一个儿子节点的black depth加一,于是不难证明
  • \(A\)\(ARB_{h}\)的情况:
    • \(A\)至少有\(2\times(2^{h-1}-1)=2^h-2\)个内部黑节点
    • \(A\)至多有\(2\times(4^{h-1}-1)+1=\frac{4^h}{2}-1\)个内部节点
    • 由上文证明\(RB_{h}\)的第三条性质可知成立

Problem Set 4.2

Problem 4.2.1

\((1)\)\(h_1(72)=6\),插入;\(h_1(11)=0\),插入;\(h_1(42)=9\),插入;\(h_1(68)=2\),插入;\(h_1(6)=6\),插入;\(h_1(30)=8\),插入;\(h_1(47)=3\),插入;\(h_1(98)=10\),插入;\(h_1(10)=10\),插入。故最终的表如下(为了简便假设哈希表直接存链表头)
image
\((2)\)\(h_1(72)=6\),插入;\(h_1(11)=0\),插入;\(h_1(42)=9\),插入;\(h_1(68)=2\),插入;\(h_1(6)=6,h(6,1)=7\),插入;\(h_1(30)=8\),插入;\(h_1(47)=3\),插入;\(h_1(98)=10\),插入;\(h_1(10)=10,h(10,1)=0,h(10,2)=1\),插入。故最终表如下
image
\((3)\)\(h_1(72)=6\),插入;\(h_1(11)=0\),插入;\(h_1(42)=9\),插入;\(h_1(68)=2\),插入;\(h_1(6)=6,h(6,1)=2,h(6,2)=9,h(6,3)=5\),插入;\(h_1(30)=8\),插入;\(h_1(47)=3\),插入;\(h_1(98)=10\),插入;\(h_1(10)=10,h(10,1)=0,h(10,2)=1\),插入。故最终表如下
image

Problem 4.2.2

a.对于\(\alpha=0.25,0.5,1.0,2.0\)来说,closed addressing需要的空间分别为\(h_c+2n=h_c+2\alpha h_c=(1+2\alpha)h_c=1.5h_c,2h_c,3h_c,5h_c\);对应的空间分别用在open addrssing下,\(\alpha\)分别为\(\frac{\alpha h_c}{(1+2\alpha)h_c}=\frac{\alpha}{1+2\alpha}=\frac{1}{6},\frac{1}{4},\frac{1}{3},\frac{2}{5}\)
b.对于\(\alpha=0.25,0.5,1.0,2.0\)来说,closed addressing需要的空间分别为\(h_c+5n=h_c+5\alpha h_c=(1+5\alpha)h_c=2.25h_c,3.5h_c,6h_c,11h_c\);对应的空间分别用在open addrssing下,\(\alpha\)分别为\(\frac{\alpha h_c}{(1+5\alpha)h_c/4}=\frac{4\alpha}{1+5\alpha}=\frac{4}{9},\frac{4}{7},\frac{2}{3},\frac{8}{11}\)

Problem 4.2.3

假设符合一致哈希,那么有\(\frac{1}{1-\alpha}=2\cdot\frac{1}{\alpha}\ln\frac{1}{1-\alpha}\),解得\(\alpha\approx0.715\)

Problem Set 4.3

Problem 4.3.1

class UnionFind:
    def __init__(self, size):
        self.parent = list(range(size + 1)) 
        self.size = [1] * (size + 1)     
    def find(self, x):
        while self.parent[x] != x:
            x = self.parent[x]
        return x
    def wUnion(self, a, b):
        root_a = self.find(a)
        root_b = self.find(b)
        if root_a == root_b:
            return
        if self.size[root_a] < self.size[root_b]:
            root_a, root_b = root_b, root_a
        self.parent[root_b] = root_a
        self.size[root_a] += self.size[root_b]

Problem 4.3.2

使用数学归纳法。对于\(k=0\)显然成立。假设对\(\forall k<c\)时都成立,对于\(k=c\)时,由\(T_k\)的构造方式可知,其节点数为\(2\times2^{k-1}=2^k\),高度为\(k-1+1=k\),在高度\(k\)只有一个节点,也就是\(T_{k-1}\)中高度为\(k-1\)的节点(因为被附加的\(T_{k-1}\)的所有节点的深度都加一,另一个\(T_{k-1}\)的所有节点的深度不变,而被附加的\(T_{k-1}\)只有一个节点的深度为\(k-1\)

Problem 4.3.3

建立\(n\)个点,对于每一个等于的约束,将等号两边的点用并查集合并;处理完所有等于约束之后,再依次检查每个不等于约束是否成立(不等号两边的元素如果在同一集合中就不成立),如果都成立那么就可以同时满足,否则就不行

class UnionFind:
    def __init__(self, size):
        self.parent = list(range(size))
        self.rank = [0] * size
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x]) 
        return self.parent[x]
    def union(self, x, y):
        root_x = self.find(x)
        root_y = self.find(y)
        if root_x == root_y:
            return
        if self.rank[root_x] < self.rank[root_y]:
            self.parent[root_x] = root_y
        else:
            self.parent[root_y] = root_x
            if self.rank[root_x] == self.rank[root_y]:
                self.rank[root_x] += 1

def var_to_index(var):
    return int(var[1:]) - 1

def can_satisfy_constraints(n, constraints):
    uf = UnionFind(n)
    inequalities = []
    for constraint in constraints:
        a, op, b = constraint.split()
        if op == '=':
            idx_a = var_to_index(a)
            idx_b = var_to_index(b)
            uf.union(idx_a, idx_b)
        else: 
            inequalities.append((a, b))
    for a, b in inequalities:
        idx_a = var_to_index(a)
        idx_b = var_to_index(b)
        if uf.find(idx_a) == uf.find(idx_b):
            return False
    return True

Problem 4.3.4

时间复杂度为\(O(n)\)。均摊操作如下:在一次加法中,对于每个进位的位置(此时这些位置都是\(2\)),将其进位的复杂度摊到其前面最晚的一次从\(1\)变成\(2\)的时候
实际费用:\(t+1\)
会计费用:\(1-t\)(当且仅当当前加法操作涉及到的最高位为\(1\)的时候才会在\(-t\)前面加一)
均摊费用:\(2=O(1)\)
所以在实施\(n\)次操作之后,时间复杂度为\(O(n)\)

Problem 4.3.5

可以用一个链表来实现。对于插入操作,简单地插入就好了;对于删除操作,首先使用选择算法线性找出第\(\lceil\frac{|s|}{2}\rceil\)大,设为\(x\),然后遍历链表将严格大于\(x\)的数删掉,同时记录删除的数的个数,设为\(c\),然后再遍历链表,删除\(\lceil\frac{|s|}{2}\rceil-c\)\(x\)即可。每次删除操作的\(O(n)\)时间复杂度可以均摊到被删除的数上,所以平摊代价为\(O(1)\).下面是均摊操作:

  • 插入操作
    • 实际费用:\(t_1\)\(t_1\)是将元素插入到链表中的费用)
    • 会计费用:\(t_2\)\(t_2\)是某次删除操作中,当前插入的元素被删除了,那一次删除操作的总费用均摊到这个元素上面的费用)
    • 均摊费用:\(t_1+t_2=O(1)\)
  • 删除操作
    • 实际费用:\(\frac{t_2}{2}|s|\)
    • 会计费用:\(-\frac{t_2}{2}|s|\)(此时被删除的\(\lceil\frac{|s|}{2}\rceil\)个元素,每个元素均摊到的费用为\(t_2\)
    • 均摊费用:\(0\)
class Node:
    def __init__(self, value):
        self.value = value
        self.prev = None
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0
    def insert(self, value):
        new_node = Node(value)
        if self.head is None:
            self.head = self.tail = new_node
        else:
            new_node.next = self.head
            self.head.prev = new_node
            self.head = new_node
        self.size += 1
    def to_list(self):
        result = []
        current = self.head
        while current:
            result.append(current.value)
            current = current.next
        return result
    def delete_node(self, node):
        if node.prev:
            node.prev.next = node.next
        else:
            self.head = node.next
        if node.next:
            node.next.prev = node.prev
        else:
            self.tail = node.prev
        self.size -= 1
def del_larger_half(ll):
    if ll.size == 0:
        return
    k = (ll.size + 1) // 2 
    arr = ll.to_list()     
    x = 选择算法
    to_delete = []
    current = ll.head
    while current:
        if current.value > x:
            to_delete.append(current)
        current = current.next
    for node in to_delete:
        ll.delete_node(node)
    deleted = len(to_delete)
    if deleted < k:
        remaining = k - deleted
        current = ll.head
        to_delete = []
        while current and remaining > 0:
            if current.value == x:
                to_delete.append(current)
                remaining -= 1
            current = current.next
        for node in to_delete:
            ll.delete_node(node)
posted @ 2025-03-30 10:26  最爱丁珰  阅读(26)  评论(0)    收藏  举报