Python数据结构7:无序表,链表对象增删查,链表结点,代码实现

本文介绍了无序表的概念,特别是使用Python实现无序表的链表数据结构。通过链表节点类Node的定义,展示了如何创建、添加、查找、删除数据项。此外,还详细解释了链表的头节点、size、search和remove方法的实现,提供了具体的代码示例。

1. 无序表列表:

1.1 定义:

列表是一种数据项按照相对位置存放的数据集。

无序表unordered list是指数据项之间没有顺序的列表。
如一个考试分数的集合“54, 26, 93, 17, 77和31”。
用无序表来表示,就是[54, 26, 93,17, 77, 31]。

2. Python中的无序表List

2.1 定义

List由如下操作定义,为了简单起见,假设表中不存在重复数据项:

  • List():创建一个空列表
  • add(item):添加一个数据项到列表中,假设item原先不存在于列表中
  • remove(item):从列表中移除item,列表被修改,item原先应存在于表中
  • search(item):在列表中查找item,返回布尔类型值
  • isEmpty():返回列表是否为空
  • size():返回列表包含了多少数据项
  • append(item):添加一个数据项到表末尾,假设item原先不存在于列表中
  • index(item):返回数据项在表中的位置
  • insert(pos, item):将数据项插入到位置pos,假设item原先不存在与列表中,同时原列表具有足够多个数据项,能让item占据位置pos
  • pop():从列表末尾移除数据项,假设原列表至少有1个数据项
  • pop(pos):移除位置为pos的数据项,假设原列表存在位置pos

3. 链表

3.1 定义和性质

链表可以实现一个无序表数据格式。

虽然列表数据结构要求保持数据项的前后相对位置,但这种前后位置的保持,并要求数据项依次存放在连续的存储空间

如下图,数据项存放位置并没有规则,但如果在数据项之间建立链接指向,就可以保持其前后相对位置

  • 第一个和最后一个数据项需要显式标记出来,一个是队首,一个是队尾,后面再无数据了。

3.2 节点Node

链表由节点Node组成

每个节点Node包含两部分:

  • 数据项本身: data
  • 指向下一个节点的引用信息: next。
    注意next为None的意思是没有下一个节点了,这个很重要

3.3 Python实现链表的节点Node

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

    def get_data(self):
        return self.data

    def get_next(self):
        return self.next

    def set_data(self, new_data):
        self.data = new_data

    def set_next(self, new_next):
        self.next = new_next


node1 = Node(54)
print(node1.get_data())  # 54 

node2 = Node(26)
node1.set_next(node2)
print(node2.get_data())  # 26
print(node1.get_next().get_data())  # 26

node3 = Node(93)
node2.set_next(node3)
print(node3.get_data())  # 93
print(node2.get_next().get_data())  # 93

3.4 用链表节点Node实现无序表

链表的第一个和最后一个节点最重要

如果想访问到链表中的所有节点,就必须从第一个节点开始沿着链接遍历下去,就是说从头节点开始,第一个next开始遍历无序表(即头节点的next的节点的数据被访问),直到next是None的时候遍历停止。

  • 所以无序表必须要有对第一个节点的引用信息

3.4.1 头节点head

头节点就是用来保存列表中第一个数据的引用信息的,空表也有head,空表的head是None。意思是self.head就相当于self.head.next(),由于self.head.data()是不存在的,所以这部分就直接省略了。换句话说self.head()就表示了第一个存储数据的节点就等同于self.head.next()。

class UnorderList:
	def __init__(self):
		self.head = None
mylist = UnorderList()
print mylist.head # 结果为None

随着数据项的加入,无序表的head始终指向链条中的第一个节点

  • 注意无序表mylist对象本身并head不包含数据项(数据项在节点中)
  • head只是对首个节点Node的引用
  • 判断空表的isEmpty()很容易实现
return self.head == None

3.4.2 add方法

add方法:向无序表中添加数据项

无序表并没有限定数据项之间的顺序,新数据项可以加入到原表的任何位置

按照实现的性能考虑,应添加到最容易加入的位置上。

要访问到整条链上的所有数据项,必须从表头head开始沿着next链接逐个向后查找,所以添加新数据项最快捷的位置是表头,整个链表的首位置。

def add(self, item):
	temp = Node(item)
	temp.setNext(self.head)
	self.head = temp

3.4.3 size方法

size:从链条头head开始遍历到表尾同时用变量累加经过的节点个数

def size(self):
	current = self.head
	count = 0
	while current != None:
		count = count + 1
		current = current.getNext()
	return count 

3.4.4 search方法

从链表头head开始遍历到表尾,同时判断当前节点的数据项是否为目标

def search(self, item)
	current = self.head
	found = False
	while current != None and not found:
		if current.getData() == item:
			found = True
		else:
			current = current.getNext()
	return found	

3.4.5 remove方法

首先要找到item,这个过程跟search一样,但在删除节点时,需要特别的技巧

  • current指向的是当前匹配数据项的节点
  • 而删除需要把前一个节点的next指向current的下一个节点
  • 所以我们在search current的同时,还要维护前一个(previous)节点的引用

找到item之后,current指向item节点,previous指向前一个节点,开始执行删,需要区分两种情况:

  • current是首个节点
  • current是位于链条中间的节点
def remove(item):
	current = self.head
	previous = None
	found = False
	while not found:
		if current.getData() == item:
			found = True
		else:
			previous = current
			current = current.getNext()
		if previous == None:
			self.head = current.getNext()
		else:
			previous.setNext(current.getNext())

整个链表的对象和操作和节点定义如下:

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

    def get_data(self):
        return self.data

    def get_next(self):
        return self.next

    def set_data(self, new_data):
        self.data = new_data

    def set_next(self, new_next):
        self.next = new_next


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

    def add(self, item):
        temp = Node(item)
        temp.set_next(self.head)
        self.head = temp

    def size(self):
        count = 0
        current = self.head
        while current.next is not None:
            count = count + 1
            current = current.next
        return count

    def search(self, item):
        current = self.head
        found = False
        while current.next is not None:
            current = current.next
            if current.data == item:
                return current.data
        return found

    def remove(self, item):
        current = self.head
        if current.data == item:
            self.head = current.next
        else:
            previous = current
            current = current.next
            while current.next is not None:
                previous = previous.next
                current = current.next
                if current.data == item:
                    previous.next = current.next


my_list = UnorderList()
print(my_list.head)
my_list.add(31)
my_list.add(77)
my_list.add(17)
my_list.add(93)
my_list.add(26)
my_list.add(54)
print(my_list.head.get_data())
print(my_list.size())
print(my_list.search(31))
my_list.remove(31)
print(my_list.size())
print(my_list.search(31))
print(my_list.remove(93))
print(my_list.size())
print(my_list.search(93))

参考文献

本文的知识来源于B站视频 【慕课+课堂实录】数据结构与算法Python版-北京大学-陈斌-字幕校对-【完结!】,是对陈斌老师课程的复习
总结

<think> 我们首先需要设计一个菜单,让用户可以选择不同的链表操作功能。菜单应包含以下选项: 1. 创建含正整数结点的无序链表 2. 输出链表结点 3. 链表结点升序排序 4. 链表结点插入 5. 链表结点删除 6. 链表逆序(结构上逆序) 7. 链表拆分(例如按奇偶性拆分) 0. 退出 我们将使用单向链表来存储数据。每个结点包含一个整数值和一个指向下一个结点的指针。 由于菜单需要循环显示直到用户选择退出,因此我们使用一个循环来显示菜单并处理用户输入。 我们将为每个功能编写单独的函数。 注意:在操作链表之前,必须已经创建了链表(至少是空链表)。因此,在程序开始时会初始化一个空链表。 我们还需要考虑链表的持久性,即每次操作都是对同一个链表进行操作(除了拆分操作可能会产生新链表,但这里我们按题目要求,拆分后原链表可能被分成两个链表,但题目没有明确,我们假设拆分后原链表不再保留,而是变成两个子链表?或者拆分操作后返回两个链表?但菜单要求是连续的,所以我们可以考虑将拆分后的两个链表分别存储,但这样后续操作会变得复杂。因此,为了简化,我们只保留一个主链表,拆分操作后我们将输出两个链表,但程序继续操作的是主链表(我们可以选择操作其中一个?或者拆分后让用户选择操作哪个?这样会使得菜单更复杂,所以我们可以选择在拆分后,将其中一个链表存储到另一个变量,但后续操作只能操作主链表,另一个链表将被忽略?这不太符合实际。另一种做法是拆分操作仅输出两个链表,但不改变主链表,这样后续操作还是对主链表进行?但是题目要求拆分功能,所以我们可以这样设计:拆分后,将原链表拆分成两个链表,然后程序结束?或者我们要求用户选择后续操作哪个链表?这样会使得程序变得复杂。 考虑到菜单的简洁性,我们这里对拆分功能做如下处理: 拆分功能将链表拆分成两个链表(比如按奇偶性拆分),然后分别输出这两个链表,但是拆分操作后,我们并不改变原来的链表(即不破坏原链表),或者我们可以选择将原链表清空,然后用两个新链表代替?但是后续操作只能操作一个链表,所以这里我们只输出拆分后的两个链表,然后继续使用原链表(即拆分操作不会改变主链表,只是输出拆分结果)。或者我们可以将拆分后的链表存储到两个全局变量中,然后让用户选择后续操作哪个链表?这样会使得程序状态管理复杂。 因此,我们简化处理:拆分操作仅输出两个链表,但不改变当前操作的链表(即主链表不变)。这样,用户执行拆分操作后,可以继续对主链表进行操作。 另一种方案:拆分操作后,我们可以让用户选择将其中一个链表设置为当前链表,但这样需要增加状态,我们这里先不实现。 我们按照以下步骤: 1. 定义链表结点结构。 2. 初始化一个空链表(头指针)。 3. 显示菜单,根据用户选择调用相应函数。 由于有多个操作,我们用一个循环来持续显示菜单直到用户选择退出。 注意:插入和删除操作需要用户输入要插入或删除的数值。 我们这里假设链表中的数值可以重复,所以插入和删除时,删除第一个遇到的指定数值的结点。 升序排序:我们将实现一个排序算法(比如冒泡排序、插入排序等)或者将链表转化为数组排序再重新构建链表。为了简单,我们使用选择排序或插入排序。但考虑到效率,我们可以用归并排序,但这里为了简单,我们使用插入排序(因为链表插入操作方便)。 链表逆序:我们将链表的结构逆序。 链表拆分:例如,将链表拆分为奇数链表和偶数链表(根据结点值的奇偶性),并输出这两个链表,但原链表保持不变。 下面是具体的代码结构(使用Python): 注意:我们使用全局变量`head`来表示当前操作的链表。同时,对于拆分操作,我们不改变`head`,只是输出两个新链表。 由于菜单需要循环,我们使用while循环。 另外,我们还需要一个创建链表的函数,用户输入一系列正整数,以非正整数(如0或负数)结束输入。 输出链表:遍历链表并打印每个结点的值。 升序排序:我们将使用插入排序来实现链表的排序(因为插入排序在链表实现相对高效且简单)。 插入结点:用户输入一个正整数,将其插入到链表中(由于链表无序,我们可以插入到头部或尾部,但为了简单,我们插入到头部)。 删除结点:用户输入一个正整数,删除链表中第一个遇到的该值的结点。 逆序:将链表结构逆序。 拆分:按结点值的奇偶性拆分,奇数结点组成一个链表,偶数结点组成一个链表,并分别输出,但原链表不变。 但是注意:如果我们在拆分操作中不改变原链表,那么拆分操作只是遍历原链表并创建两个新链表,然后输出。这样原链表仍然存在。 我们开始编写代码。 注意:菜单选项0为退出。 由于程序较长,我们分函数编写。 另外,我们还需要注意内存管理,在删除链表结点时释放内存(在Python中由于有垃圾回收,我们不需要显式释放,但为了规范,我们使用del删除结点,但实际在Python中,当没有引用指向对象时,对象会被自动回收)。 下面我们用Python实现。 </think>以下是一个实现链表操作菜单的程序设计,包含您要求的所有功能: ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next class LinkedListMenu: def __init__(self): self.head = None # 1. 创建无序链表 def create_list(self): """创建含正整数结点的无序链表""" self.head = None print("输入正整数序列(以-1结束):") values = [] while True: try: num = int(input()) if num == -1: break if num > 0: values.append(num) except: print("请输入有效正整数!") # 使用头插法创建无序链表 for val in values: new_node = ListNode(val) new_node.next = self.head self.head = new_node print("链表创建完成!") # 2. 输出链表结点 def print_list(self, head=None): """输出链表结点""" if not head: head = self.head if not head: print("链表为空") return cur = head while cur: print(cur.val, end=" -> " if cur.next else "\n") cur = cur.next # 3. 链表升序排序 def sort_list(self): """链表结点升序排序(插入排序实现)""" if not self.head or not self.head.next: return dummy = ListNode(0) dummy.next = self.head last_sorted = self.head cur = last_sorted.next while cur: if last_sorted.val <= cur.val: last_sorted = last_sorted.next else: prev = dummy while prev.next.val <= cur.val: prev = prev.next last_sorted.next = cur.next cur.next = prev.next prev.next = cur cur = last_sorted.next self.head = dummy.next print("链表已排序") # 4. 链表结点插入 def insert_node(self): """链表结点插入""" if not self.head: print("链表为空,请先创建链表") return try: val = int(input("输入要插入的值: ")) pos = int(input("输入插入位置(0表示头部): ")) except: print("输入无效!") return new_node = ListNode(val) # 头部插入 if pos == 0: new_node.next = self.head self.head = new_node print(f"已插入头部") return # 查找插入位置 cur = self.head count = 0 while cur and count < pos - 1: cur = cur.next count += 1 if not cur: print("插入位置超出链表长度") return new_node.next = cur.next cur.next = new_node print(f"已在位置 {pos} 插入") # 5. 链表结点删除 def delete_node(self): """链表结点删除""" if not self.head: print("链表为空") return try: val = int(input("输入要删除的值: ")) except: print("输入无效!") return # 处理头部删除 while self.head and self.head.val == val: self.head = self.head.next cur = self.head while cur and cur.next: if cur.next.val == val: cur.next = cur.next.next else: cur = cur.next print(f"已删除所有值为 {val} 的结点") # 6. 链表逆序 def reverse_list(self): """链表逆序""" prev = None cur = self.head while cur: next_node = cur.next cur.next = prev prev = cur cur = next_node self.head = prev print("链表已逆序") # 7. 链表拆分 def split_list(self): """链表拆分(按奇偶性)""" if not self.head: print("链表为空") return odd_dummy = ListNode(0) # 奇数链表 even_dummy = ListNode(0) # 偶数链表 odd_cur = odd_dummy even_cur = even_dummy cur = self.head while cur: if cur.val % 2 == 1: # 奇数 odd_cur.next = cur odd_cur = odd_cur.next else: # 偶数 even_cur.next = cur even_cur = even_cur.next cur = cur.next # 设置链表尾部 odd_cur.next = None even_cur.next = None print("\n奇数链表:") self.print_list(odd_dummy.next) print("\n偶数链表:") self.print_list(even_dummy.next) # 菜单显示 def show_menu(self): while True: print("\n======== 链表操作菜单 ========") print("1. 创建含正整数结点的无序链表") print("2. 输出链表结点") print("3. 链表结点升序排序") print("4. 链表结点插入") print("5. 链表结点删除") print("6. 链表逆序") print("7. 链表拆分") print("0. 退出") choice = input("请选择操作: ") if choice == '0': print("程序退出") break elif choice == '1': self.create_list() elif choice == '2': self.print_list() elif choice == '3': self.sort_list() elif choice == '4': self.insert_node() elif choice == '5': self.delete_node() elif choice == '6': self.reverse_list() elif choice == '7': self.split_list() else: print("无效选择,请重新输入") # 启动菜单 if __name__ == "__main__": menu = LinkedListMenu() menu.show_menu() ``` ### 功能说明 1. **创建无序链表**: - 用户输入正整数序列,以-1结束 - 使用头插法创建无序链表[^1] - 示例输入:`5 3 8 2 -1` → 生成链表:`2->8->3->5` 2. **输出链表结点**: - 遍历并打印链表所有结点值 - 空链表提示信息 3. **链表升序排序**: - 使用插入排序算法实现$O(n^2)$时间复杂度[^2] - 示例:输入`2->8->3->5` → 输出`2->3->5->8` 4. **链表结点插入**: - 支持头部插入和指定位置插入 - 位置索引从0开始计数 5. **链表结点删除**: - 删除所有匹配值的结点 - 处理头部连续删除的情况 6. **链表逆序**: - 使用迭代法反转链表指针方向[^3] - 示例:`1->2->3` → `3->2->1` 7. **链表拆分**: - 按结点值的奇偶性拆分 - 分别输出奇数链表和偶数链表 - 示例:`1->2->3->4` → 奇数链`1->3`,偶数链`2->4` ### 使用说明 1. 运行程序后显示操作菜单 2. 选择1创建链表后才能执行其他操作 3. 每次操作后可通过选项2查看当前链表状态 4. 输入0退出程序 这个实现链表操作封装为类方法,通过菜单界面交互,符合结构化编程规范,所有功能都经过模块化设计,便于维护和扩展。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值