数据结构期末复习(9)

目录

题目15:在长度为 n (n ≥ 1) 的双链表 L 中,删除尾结点的时间复杂度为()。

选项:

正确答案:A

详细解析:

双链表简介:

删除尾结点的操作:

题目分析:

代码示例:

时间复杂度分析:

结论:

题目18:对 n 个不同的排序码进行冒泡排序,在元素无序的情况下比较的次数为()。

选项:

正确答案:A

详细解析:

冒泡排序简介:

比较次数分析:

时间复杂度:

代码示例:

时间复杂度确定方法:

结论:

题目21:给定有 n 个元素向量,建立一个有序单链表的时间复杂度是()。

选项:

正确答案:D

详细解析:

建立有序单链表的过程:

题目分析:

代码示例:

时间复杂度分析:

总结表格:

结论:

题目25:在一个具有 n 个结点的有序单链表中插入一个新结点并仍然有序的时间复杂度是()。

选项:

正确答案:D

详细解析:

有序单链表简介:

插入新结点的操作步骤:

时间复杂度分析:

代码示例:

时间复杂度确定方法:

总结表格:

结论:

题目56:在单链表中删除 p 所指结点的后继结点,该算法的时间复杂度是()。

选项:

正确答案:A

详细解析:

单链表简介:

删除操作简介:

题目具体要求:

操作步骤:

时间复杂度分析:

代码示例:

时间复杂度确定方法:

可能的误区:

结论:

综合总结

时间复杂度总结表

如何确定时间复杂度

代码与时间复杂度结合示例

附加说明:

链表操作时间复杂度对比

冒泡排序时间复杂度详解

总结


题目15:在长度为 n (n ≥ 1) 的双链表 L 中,删除尾结点的时间复杂度为()。

选项:

  • A. O(n)
  • B. O(n log₂n)
  • C. O(n²)
  • D. O(1)

正确答案:A

详细解析:

双链表简介:

双链表(Doubly Linked List)是一种链式数据结构,每个节点包含两个指针:

  • prev:指向前驱节点。
  • next:指向后继节点。
删除尾结点的操作:
  1. 有尾指针的双链表:
    • 时间复杂度:O(1)
    • 原因: 直接通过尾指针访问尾结点,无需遍历链表。
  2. 无尾指针的双链表:
    • 时间复杂度:O(n)
    • 原因: 需要从头遍历整个链表找到尾结点的前驱节点。
题目分析:

题目给出的正确答案是 A. O(n),这意味着假设链表 没有维护尾指针。因此,删除尾结点需要遍历整个链表,时间复杂度为O(n)。

代码示例:

假设我们使用Python来实现双链表的删除尾结点操作。

 
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

class DoublyLinkedList:
    def __init__(self):
        self.head = None
        # self.tail = None  # 假设没有维护尾指针

    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
            return
        current = self.head
        while current.next:
            current = current.next
        current.next = new_node
        new_node.prev = current

    def delete_tail(self):
        if not self.head:
            return
        current = self.head
        while current.next:
            current = current.next
        if current.prev:
            current.prev.next = None
        else:
            self.head = None  # 链表只有一个节点

# 使用示例
dll = DoublyLinkedList()
for i in range(1, 6):
    dll.append(i)
dll.delete_tail()

时间复杂度分析:
  • 遍历链表找到尾结点: O(n)
  • 删除尾结点: O(1)
  • 总时间复杂度: O(n) + O(1) = O(n)
结论:

由于假设双链表没有维护尾指针,删除尾结点需要遍历整个链表,时间复杂度为O(n),因此选项A正确。


题目18:对 n 个不同的排序码进行冒泡排序,在元素无序的情况下比较的次数为()。

选项:

  • A. n(n-1)/2
  • B. n+1
  • C. n
  • D. n−1

正确答案:A

详细解析:

冒泡排序简介:

冒泡排序(Bubble Sort)是一种简单的排序算法,通过重复交换相邻未按顺序排列的元素,将最大的元素“冒泡”到序列的末端。其基本步骤如下:

  1. 从头开始,依次比较相邻的元素。
  2. 如果前一个元素大于后一个元素,则交换它们。
  3. 重复以上过程,直到整个序列有序。
比较次数分析:

在最坏情况下(即元素完全逆序),冒泡排序需要进行最大数量的比较和交换。

  • 第一趟: 比较n-1次,将最大的元素移动到最后。
  • 第二趟: 比较n-2次,将第二大的元素移动到倒数第二个位置。
  • ...
  • 第(n-1)趟: 比较1次,将最小的元素移动到第一个位置。

因此,总的比较次数为: (n−1)+(n−2)+⋯+1=n(n−1)2(n-1) + (n-2) + \cdots + 1 = \frac{n(n-1)}{2}(n−1)+(n−2)+⋯+1=2n(n−1)​

时间复杂度:

冒泡排序的时间复杂度在最坏情况下是 O(n²),因为比较次数与n的平方成正比。

代码示例:

以下是Python中冒泡排序的实现及比较次数的统计。

def bubble_sort(arr):
    n = len(arr)
    comparison_count = 0
    for i in range(n):
        for j in range(0, n-i-1):
            comparison_count += 1
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return comparison_count

# 使用示例
arr = [5, 1, 4, 2, 8]
comparisons = bubble_sort(arr)
print(f"排序后的数组: {arr}")
print(f"比较次数: {comparisons}")

输出:

排序后的数组: [1, 2, 4, 5, 8]
比较次数: 10

对于n=5,比较次数为5*4/2 = 10,符合公式。

时间复杂度确定方法:
  • 外层循环: n次
  • 内层循环: 每次减少一次,共(n-1)次
  • 总比较次数: n(n-1)/2 = O(n²)
结论:

在最坏情况下,冒泡排序需要进行n(n-1)/2次比较,因此选项A正确。


题目21:给定有 n 个元素向量,建立一个有序单链表的时间复杂度是()。

选项:

  • A. O(1)
  • B. O(n²)
  • C. O(n log₂ n)
  • D. O(n)**

正确答案:D

详细解析:

建立有序单链表的过程:
  1. 假设输入向量已经有序:

    • 时间复杂度:O(n)
    • 原因: 直接遍历向量,将元素依次插入链表,无需比较。
  2. 输入向量无序:

    • 时间复杂度:O(n log n)
    • 原因: 需要先对向量进行排序,然后再构建链表。
题目分析:

题目并未明确说明输入向量是否有序。根据给定答案D. O(n),我们假设输入向量已经有序,因此可以在O(n)时间内构建有序单链表。

代码示例:

以下是Python中将有序向量构建为单链表的实现。

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class SortedLinkedList:
    def __init__(self):
        self.head = None

    def build_from_sorted_vector(self, vec):
        for data in vec:
            self.append(data)

    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
            return
        current = self.head
        while current.next:
            current = current.next
        current.next = new_node

    def print_list(self):
        current = self.head
        elems = []
        while current:
            elems.append(current.data)
            current = current.next
        print(" -> ".join(map(str, elems)))

# 使用示例
vec = [1, 2, 3, 4, 5]  # 已排序
sll = SortedLinkedList()
sll.build_from_sorted_vector(vec)
sll.print_list()

输出:

1 -> 2 -> 3 -> 4 -> 5

时间复杂度分析:
  • 构建链表: 遍历向量一次,逐个插入链表末尾,每次插入为O(1)(因为维护了尾指针)。
  • 总时间复杂度: O(n)
总结表格:
情况操作时间复杂度
输入向量已排序逐个插入链表末尾O(n)
输入向量无序先排序(O(n log n))+ 构建链表(O(n))O(n log n)
结论:

根据题目提供的正确答案D. O(n),假设输入向量已经有序,因此建立一个有序单链表的时间复杂度为O(n)


题目25:在一个具有 n 个结点的有序单链表中插入一个新结点并仍然有序的时间复杂度是()。

选项:

  • A. O(n log₂ n)
  • B. O(n²)
  • C. O(1)
  • D. O(n)**

正确答案:D

详细解析:

有序单链表简介:

有序单链表是一种链式数据结构,其中元素按一定顺序(通常是升序或降序)排列。每个节点只有一个指向后继节点的指针。

插入新结点的操作步骤:
  1. 查找插入位置:

    • 从头节点开始,遍历链表,找到第一个比新结点大的节点,插入到其前面。
    • 如果新结点比所有现有节点都大,插入到链表末尾。
  2. 执行插入:

    • 修改前一个节点的next指针,使其指向新结点。
    • 将新结点的next指针指向后一个节点。
时间复杂度分析:
  • 查找插入位置: 最坏情况下需要遍历整个链表,时间复杂度为O(n)。
  • 执行插入: 直接操作指针,时间复杂度为O(1)。
  • 总时间复杂度: O(n) + O(1) = O(n)
代码示例:

以下是Python中在有序单链表中插入新结点的实现。

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class SortedLinkedList:
    def __init__(self):
        self.head = None

    def insert_sorted(self, data):
        new_node = Node(data)
        # 如果链表为空或新节点应插在头部
        if not self.head or data < self.head.data:
            new_node.next = self.head
            self.head = new_node
            return
        # 遍历链表找到插入位置
        current = self.head
        while current.next and current.next.data < data:
            current = current.next
        # 插入新节点
        new_node.next = current.next
        current.next = new_node

    def print_list(self):
        current = self.head
        elems = []
        while current:
            elems.append(current.data)
            current = current.next
        print(" -> ".join(map(str, elems)))

# 使用示例
sll = SortedLinkedList()
for data in [1, 3, 5, 7]:
    sll.insert_sorted(data)
sll.print_list()
sll.insert_sorted(4)
sll.print_list()

1 -> 3 -> 5 -> 7 1 -> 3 -> 4 -> 5 -> 7

时间复杂度确定方法:
  • 查找插入位置: 最坏情况下需要遍历整个链表,O(n)。
  • 指针操作: O(1)。
  • 总时间复杂度: O(n)
总结表格:
操作类型操作描述时间复杂度
插入操作找到插入位置并插入结点O(n)
结论:

在有序单链表中插入一个新结点并保持有序的时间复杂度为O(n),因此选项D正确。


题目56:在单链表中删除 p 所指结点的后继结点,该算法的时间复杂度是()。

选项:

  • A. O(1)
  • B. O(n)
  • C. O(log₂n)
  • D. O(n²)

正确答案:A

详细解析:

单链表简介:

单链表(Singly Linked List)是一种链式数据结构,每个节点包含一个数据域和一个指向下一个节点的指针(next)。与双链表不同,单链表只支持单向遍历。

删除操作简介:

在单链表中删除一个节点,通常需要访问该节点的前驱节点,以便更新前驱节点的next指针指向被删除节点的后继节点。

题目具体要求:

删除节点p所指结点的后继结点。

操作步骤:
  1. 访问后继结点:

    • 直接通过p的next指针访问p的后继结点,即q = p.next
  2. 更新指针:

    • 将p的next指针指向q.next,即p.next = q.next
  3. 删除结点:

    • 如果需要,可以释放q的内存(在某些编程语言中,如C/C++)。
时间复杂度分析:
  • 访问p和q: 由于已知p的位置,直接通过指针访问q是O(1)时间。
  • 更新指针: 这也是O(1)时间。
  • 删除操作: 释放内存或其他相关操作通常也是O(1)。

因此,整个删除操作的时间复杂度是 O(1)

代码示例:

以下是Python中在单链表中删除p所指结点的后继结点的实现。

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class SinglyLinkedList:
    def __init__(self):
        self.head = None

    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
            return
        current = self.head
        while current.next:
            current = current.next
        current.next = new_node

    def delete_successor(self, p):
        if p and p.next:
            q = p.next
            p.next = q.next
            del q  # Python会自动管理内存

    def print_list(self):
        current = self.head
        elems = []
        while current:
            elems.append(current.data)
            current = current.next
        print(" -> ".join(map(str, elems)))

# 使用示例
sll = SinglyLinkedList()
for data in [1, 2, 3, 4, 5]:
    sll.append(data)
sll.print_list()  # 输出: 1 -> 2 -> 3 -> 4 -> 5

# 假设p指向节点3
p = sll.head.next.next  # 节点3
sll.delete_successor(p)
sll.print_list()  # 输出: 1 -> 2 -> 3 -> 5
时间复杂度确定方法:
  • 访问结点p及其后继结点q: O(1)
  • 更新指针和删除操作: O(1)
  • 总时间复杂度: O(1)
可能的误区:

有些人可能认为需要遍历链表才能找到p的前驱节点,从而导致时间复杂度为O(n)。然而,在本题中,p的位置是已知的,无需遍历链表,因此操作是常数时间的。

结论:

在单链表中删除p所指结点的后继结点的时间复杂度是O(1),因此选项A正确。


综合总结

为了更清晰地理解各个操作的时间复杂度,我们将以下列表格进行总结,并附上相应的代码示例。

时间复杂度总结表

题号操作描述时间复杂度说明代码示例
15删除双链表的尾结点(假设无尾指针)O(n)需要遍历链表找到尾结点的前驱节点见题目15代码
18冒泡排序在元素完全无序情况下的比较次数O(n²)冒泡排序在最坏情况下需要进行n(n-1)/2次比较见题目18代码
21建立一个有序单链表(假设输入向量已排序)O(n)逐个插入元素到链表末尾,时间与元素数量线性相关见题目21代码
25在有序单链表中插入一个新结点并保持有序O(n)需要遍历链表找到插入位置,最坏情况下遍历整个链表见题目25代码
56删除单链表中节点p的后继结点O(1)已知p的位置,直接通过指针操作删除后继结点,无需遍历链表见题目56代码

如何确定时间复杂度

  1. 分析操作步骤:

    • 分解操作步骤,确定每一步的时间复杂度。
    • 考虑最坏情况下的时间复杂度。
  2. 识别循环和递归:

    • 循环次数直接影响时间复杂度。
    • 嵌套循环导致时间复杂度的乘法增加。
  3. 利用已知的算法复杂度:

    • 如排序算法、搜索算法等的已知时间复杂度。
  4. 常数时间操作:

    • 指针操作、赋值等通常为O(1)时间。
  5. 结合数据结构特性:

    • 链表的遍历、插入、删除操作的时间复杂度取决于是否有辅助指针(如尾指针)或额外信息。

代码与时间复杂度结合示例

题目25为例,我们在有序单链表中插入一个新结点:

 

python

复制代码

def insert_sorted(self, data): new_node = Node(data) if not self.head or data < self.head.data: new_node.next = self.head self.head = new_node return current = self.head while current.next and current.next.data < data: current = current.next new_node.next = current.next current.next = new_node

  • if 语句: 最好情况下(新结点插入头部),时间复杂度为O(1)。
  • while 循环: 最坏情况下需要遍历整个链表,时间复杂度为O(n)。
  • 指针操作: O(1)。

因此,总时间复杂度为 O(n)


附加说明:

链表操作时间复杂度对比

操作类型单链表(Singly Linked List)双链表(Doubly Linked List)
访问头结点O(1)O(1)
访问尾结点O(n)(无尾指针)
O(1)(有尾指针)
O(1)
插入头结点O(1)O(1)
插入尾结点O(n)(无尾指针)
O(1)(有尾指针)
O(1)
删除头结点O(1)O(1)
删除尾结点O(n)O(1)
插入任意位置O(n)O(n)
删除任意位置O(n)O(n)

冒泡排序时间复杂度详解

情况时间复杂度比较次数交换次数
最好情况O(n)O(n)(已经有序)O(1)(无需交换)
平均情况O(n²)O(n²)O(n²)
最坏情况O(n²)n(n-1)/2 = O(n²)n(n-1)/2 = O(n²)

注意: 冒泡排序在元素已经有序的情况下可以优化为O(n)时间复杂度,但在题目18中明确提到“元素无序”,因此比较次数为n(n-1)/2次。


总结

通过上述详细解析、代码示例以及总结表格,我们可以清晰地理解每个操作的时间复杂度及其原因。以下是关键点的回顾:

  1. 链表操作的时间复杂度:

    • 单链表在访问尾结点时,如果没有尾指针,需要O(n)时间。
    • 双链表若维护了尾指针,删除尾结点可以在O(1)时间内完成。
  2. 排序算法的时间复杂度:

    • 冒泡排序在最坏情况下的比较次数为O(n²)。
  3. 构建有序链表的时间复杂度:

    • 如果输入数据已经有序,建立有序单链表可以在O(n)时间内完成。
    • 否则,需要先排序,时间复杂度为O(n log n)。
  4. 插入操作的时间复杂度:

    • 在有序单链表中插入新结点的时间复杂度为O(n),因为需要遍历找到插入位置。
  5. 删除操作的时间复杂度:

    • 在单链表中删除已知位置的后继结点可以在O(1)时间内完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值