数据结构-单链表

单链表的定义与案例(Python实现)

1. 结点与链表

在这里插入图片描述
链表中的基本单元是结点,上面绘制了一个基本结点的基本架构,可以看出每个结点包含两个部分:

  • val部分,是指结点的数据区域,用于存储结点本身的数据信息;它不局限于一个数据,可以是多个数据。
  • next部分,是指结点的指针域,用来存储其直接后继的结点信息;next的值指向下个结点的地址,如果当前结点为最后一个结点,next的值设置为None;
class Node:
	def __init__(self, val=0, next=None):
		self.val = val
		self.next = next

Python中,结点的基本结构如图1和Node所示;

链表是指线性表的链式存储结构;同普通的数组相比,具有以下几个优势:

  • 存储单元不一定连续,是用一组任意的存储单元来存储链表数据的;
  • 链表的长度是动态变化的,可以方便的实现节点的插入和删除;
  • 链表的每个元素都是一个节点,每个节点除存储节点数据外,还存储其直接后继关系;

头结点、头指针、首元结点

在这里插入图片描述

  • 头指针(必须有):永远指向链表从第一个结点的位置(如果链表有头结点,头指针指向头结点;否则,头指针指向首元结点)
  • 头结点(可没有):不是单链表中必须的,但是有时候在链表的第一个结点之前额外增设一个结点(数据域一般不放数据);
  • 首元结点:链表中第一个元素所在的结点,是指头结点后的第一个结点。

注意:

  • 若头结点的指针域为空,则表示该链表为空;
  • 头结点对于单链表并不是必须的;
  • 单链表中可以没有头结点,但不能没有头指针

头指针与头结点的区别

  • 头指针是一个指针,指向链表的头结点或者首元结点;
  • 头结点是一个实际存在的结点,包含指针域和数据域;
  • 头指针只声明而没有分配存储空间,但头结点进行了声明并分类了一个结点的实际无聊内存

2. 链表的构建、遍历等

2.1 新建链表, 只有简单的从头和尾部插入示范

class LinkedList(object):
	""" 构建单链表结构"""
    def __init__(self):
        self.head = None #定义头指针,并使得头指针指向链表的第一个结点
        self.length = 0

    def is_empty(self):
        # 判断链表是否为空(只需要判断头指针指向的头结点是否为空)
        return self.head == None

    def add_node_from_head(self, new_node):
        # 从头部插入结点
        if self.is_empty():
            self.head = new_node
        else:
            new_node.next = self.head
            self.head = new_node
        self.length += 1

    def add_node_from_end(self, new_node):
        # 从尾部插入结点,需要先遍历完所有结点,然后再找到最后一个结点
        if self.is_empty():
            self.head = new_node
        else:
        		 last_node = self.head
            while last_node.next is not None:
                last_node = last_node.next
            last_node.next = new_node
        self.length += 1
             
    def travel(self):
    	#遍历链表,从头指针开始,依次往下读取
        p = self.head
        while p:
            print(p.val, end=' ')
            p = p.next	

接下来,我们将0-4按顺序以end方式插入链表,并将5-9按照head方式插入链表,然后输出链表:

for t in range(10):
    new_node = Node(t)
    if t <5:
        linklist.add_node_from_end(new_node)
    else:
        linklist.add_node_from_head(new_node)

linklist.travel()

输出结果如下:

9 8 7 6 5 0 1 2 3 4

2.2 复杂的链表插入与删除

接下来,我们对链表执行一些更为复杂的操作,比如删除结点,给定插入位置等

#为了减少代码的重写率,在这里我们新建一个链表,通过继承上一个链表的属性来使用前面定义的一些方法
class LinkedList2(LinkedList):
    def __init__(self):
        super(LinkedList2, self).__init__()
        
    def add_node_from_index(self, node, index):
        if (index > (self.length+1)) or index <= 0:
            while(index > self.length + 1 or index <= 0):
                print("插入的位置超出边界,请输入[{}-{}]的范围".format(1, self.length+1))
                index = eval(input())
                
        if index == 1:
            self.add_node_from_head(node)
        elif index == 2:
            node.next = self.head.next
            self.head.next = node
            self.length += 1
        else:
            # 先遍历出当前index的前一个node;然后进行插入操作
            current_node = self.head
            for i in range(1, index-1):
                current_node = current_node.next
            node.next = current_node.next
            current_node.next = node
            self.length += 1
            
    
    def delete_node_from_index(self, index):
        # 按照索引删除结点
        if (index > self.length) or index <= 0:
            while(index > self.length or index <= 0):
                print("插入的位置超出边界,请输入[{}-{}]的范围".format(1, self.length+1))
                index = eval(input())
        if index == 1:
            self.head = self.head.next
            current_node = self.head
        elif index == 2:
            current_node = self.head
            current_node.next = current_node.next.next
        else:
            current_node = self.head
            for i in range(1, index-1):
                current_node = current_node.next
            current_node.next = current_node.next.next
        
        self.length -= 1

接下来,我们将0-4按顺序以end方式插入链表,并将5-9按照head方式插入链表,然后输出链表

9 8 7 6 5 0 1 2 3 4

然后我们在5的位置插入结点11, 删除6位置的结点, 输出结果如下:

ll2.add_node_from_index(Node(11), 5)
ll2.travel()
ll2.delete_node_from_index(6)
ll2.travel()

9 8 7 6 11 5 0 1 2 3 4
9 8 7 6 11 0 1 2 3 4

3. 单链表处理题目示例

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

解题思路

  • 首先分析题目可知,需要生成一个新链表,那么我们先定义一个新链表的头结点 head_node
  • 然后定义可移动指针prev,使其指向头结点head_node
  • 当l1或l2为空时,直接将其加入到新链表中
  • 当l1和l2均不为空时,判断l1和l2中头结点值的大小,并将prev指向较小的结点, 并将l1指向l1.next或l2指向l2.next;
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        head_node = ListNode(-1) # 构建头结点, 辅助链表构建
        prev = head_node #指针指向头结点
        
        while list1 and list2:
            if list1.val <= list2.val:
                prev.next = list1
                list1 = list1.next
            else:
                prev.next = list2
                list2 = list2.next
        if list1 is not None: # l1不为空,直接加入到链表中
            prev.next = list1
        else:
            prev.next = list2
        return head_node.next #返回除了头结点外的链表

好啦,关于单链表的相关操作和信息就分享到这啦。

如果内容有误,欢迎大家批评指正哦~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听离

请作者吃个棒棒糖吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值