Loading

单向链表


返回 我的技术栈(Technology Stack)



单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。

image
结点 实现代码(Python):

class Node(object):
    """结点"""

    def __init__(self, elem):
        self.elem = elem
        self.next = None

其中:对象属性 item 用于存放数据元素;对象属性 next 是指向下一个结点的标识
image
单链表 实现代码(Python):

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

is_empty()判断链表是否为空

image

image

实现代码(Python):

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def is_empty(self):
        """判断链表是否为空"""
        return self.__head is None

length()判断链表长度

对于下面的这样一个链表,我们计算它的length
image
为了后面更好的解释,我们把上面的图,修改下:
image
应该从头到尾遍历,
因此,引入一个cur游标变量,记录下当前结点是哪个;引入count变量,用于计算元素个数:
image

image

image

image

实现代码(Python):

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):   # node=None 是防止出现用户啥也不传入
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def length(self):
        """链表长度"""
        # cur初始时指向头节点,cur游标,用来移动遍历节点
        cur = self.__head
        # count 记录数量
        count = 0

        # 尾节点指向None,当未到达尾部时
        while cur is not None:
            count += 1
            # 将cur后移一个节点
            cur = cur.next
        return count

遍历链表

和上面判断链表长度的代码相似,就是遍历时候,我们不需要进行count计算操作
实现代码(Python):

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def travel(self):
        """遍历链表"""
        cur = self.__head
        while cur is not None:
            print(cur.elem)
            cur = cur.next

链表元素插入

尾插法

image
这里尾插法需要我们使用cur游标,遍历到最后一个元素,然后从尾部添加元素
image
实现代码(Python):

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self._head = node  # 外部不要管,不对外暴露,做成私有

    def append(self, item):
        """链表尾部插入元素"""
        node = Node(item)

        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next is not None:
                cur = cur.next
				
            cur.next = node

头插法

image

注意:顺序不能乱!

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def add(self, item):
        """链表头部插入,头插法"""
        node = Node(item)

        node.next = self.__head
        self.__head = node

阶段性测试

class Node(object):
    """节点"""

    def __init__(self, elem):
        self.elem = elem
        self.next = None


class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def is_empty(self):
        """判断链表是否为空"""
        return self.__head is None

    def length(self):
        """链表长度"""
        # cur初始时指向头节点,cur游标,用来移动遍历节点
        cur = self.__head
        # count 记录数量
        count = 0

        # 尾节点指向None,当未到达尾部时
        while cur is not None:
            count += 1
            # 将cur后移一个节点
            cur = cur.next
        return count

    def travel(self):
        """遍历链表"""
        cur = self.__head
        while cur is not None:
            print(cur.elem, end=" ")
            cur = cur.next

    def append(self, item):
        """链表尾部插入元素"""
        node = Node(item)

        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next is not None:
                cur = cur.next
            cur.next = node
			
    def add(self, item):
        """链表头部插入,头插法"""
        node = Node(item)

        node.next = self.__head
        self.__head = node


if __name__ == '__main__':
    """测试"""
    ll = SingleLinkList()
    print(ll.is_empty())
    print(ll.length())

    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)

    ll.travel()
	
运行输出:
True
0
False
1
8 1 2 3 4 5 6

指定位置添加元素

假设我们要在20与300之间添加一个元素,即 insert(2,6300)
我们需要引入一个pre变量来辅助按操作

image

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def insert(self, pos, item):
        """
        指定位置添加元素
        :param pos: 起始从0开始
        :param item: 待插入的数值
        :return:
        """

        # 处理特殊情况
        if pos <= 0:
            self.add(item)  # 头插法
        elif pos > (self.length() - 1):
            self.append(item)  # 尾插法
        else:
            pre = self.__head
            count = 0
            while count < (pos - 1):
                count += 1
                pre = pre.next
            # 当循环推出后, pre 指向 pos-1
            node = Node(item)

            node.next = pre.next
            pre.next = node

查找元素是否存在

image

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def search(self, item):
        """查找元素是否存在"""
        cur = self.__head

        # 特殊情况,空链表
        # cur = self.__head = None

        while cur is not None:
            if cur.elem == item:
                return cur.elem
            else:
                cur = cur.next

        return False

删除元素

image
image

class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def remove(self, item):
        """删除元素"""
        cur = self.__head
        pre = None
        while cur is not None:
            if cur.elem == item:
                if cur == self.__head:
                    self.__head = cur.next
                else:
                    pre.next = cur.next
            else:
                pre = cur
                cur = cur.next

完整代码(Python)

class Node(object):
    """节点"""

    def __init__(self, elem):
        self.elem = elem
        self.next = None


class SingleLinkList(object):
    """单链表"""

    def __init__(self, node=None):
        self.__head = node  # 外部不要管,不对外暴露,做成私有

    def is_empty(self):
        """判断链表是否为空"""
        return self.__head is None

    def length(self):
        """链表长度"""
        # cur初始时指向头节点,cur游标,用来移动遍历节点
        cur = self.__head
        # count 记录数量
        count = 0

        # 尾节点指向None,当未到达尾部时
        while cur is not None:
            count += 1
            # 将cur后移一个节点
            cur = cur.next
        return count

    def travel(self):
        """遍历链表"""
        cur = self.__head
        while cur is not None:
            print(cur.elem, end=" ")
            cur = cur.next
        print("\n")

    def append(self, item):
        """链表尾部插入元素"""
        node = Node(item)

        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next is not None:
                cur = cur.next
            cur.next = node

    def add(self, item):
        """链表头部插入,头插法"""
        node = Node(item)

        node.next = self.__head
        self.__head = node

    def insert(self, pos, item):
        """
        指定位置添加元素
        :param pos: 起始从0开始
        :param item: 待插入的数值
        :return:
        """

        # 处理特殊情况
        if pos <= 0:
            self.add(item)  # 头插法
        elif pos > (self.length() - 1):
            self.append(item)  # 尾插法
        else:
            pre = self.__head
            count = 0
            while count < (pos - 1):
                count += 1
                pre = pre.next
            # 当循环推出后, pre 指向 pos-1
            node = Node(item)

            node.next = pre.next
            pre.next = node

    def search(self, item):
        """查找元素是否存在"""
        cur = self.__head

        # 特殊情况,空链表
        # cur = self.__head = None

        while cur is not None:
            if cur.elem == item:
                return cur.elem
            else:
                cur = cur.next

        return False

    def remove(self, item):
        """删除元素"""
        cur = self.__head
        pre = None
        while cur is not None:
            if cur.elem == item:
                if cur == self.__head:
                    self.__head = cur.next
                else:
                    pre.next = cur.next

                break
            else:
                pre = cur
                cur = cur.next


if __name__ == '__main__':
    """测试"""
    ll = SingleLinkList()
    print(ll.is_empty())
    print(ll.length())

    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)

    ll.insert(-1, 9)
    ll.insert(2, 6300)
    ll.insert(12, 200)
    ll.travel()  # 9 8 6300 1 2 3 4 5 6 200

    ll.append(1)
    ll.travel()  # 9 8 6300 1 2 3 4 5 6 200 1
    ll.remove(1)
    ll.travel()  # 9 8 6300 2 3 4 5 6 200 1
    ll.remove(6300)
    ll.travel()  # 9 8 2 3 4 5 6 200 1
	
运行输出:
True
0
False
1
9 8 6300 1 2 3 4 5 6 200 

9 8 6300 1 2 3 4 5 6 200 1 

9 8 6300 2 3 4 5 6 200 1 

9 8 2 3 4 5 6 200 1 

链表与顺序表的对比

链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。

链表与顺序表的各种操作复杂度如下所示:

操作 链表 顺序表
访问元素 O(n) O(1)
在头部插入/删除 O(1) O(n)
在尾部插入/删除 O(n) O(1)
在中间插入/删除 O(n) (时间花在遍历上) O(n) (时间花在数据搬迁上)

注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。


posted @ 2021-05-16 10:59  言非  阅读(151)  评论(0)    收藏  举报