数据结构与算法之美

链表(三)

2020-06-12  本文已影响0人  焰火青春

我们知道顺序表存储数据时,需要一块连续的内存空间,插入删除时要进行数据搬迁,并不是那么灵活。

而现在就有这么一种数据结构, 存储时不需要是连续的内存空间存储,而是可以单独的内存空间存储。每个内存空间之间以 链条形式串起来,充分利用了计算机的内存空间,这就是 链表

链表定义

链表是一种常见的数据结构,也是一种线性表,不像顺序表一样连续存储数据,而是每个节点里存储了下一个节点的地址,类似于下面这样:

链表结构

3.1 单链表

单链表又称单向链表,顾名思义它只有一个方向。单链表每个节点包含两个域:

image

变量 p 指向头节点,需要注意的是单链表操作都是从头节点开始的。


Python 变量标识本质

Python 中存储一个变量,计算机会为其分配两块内存空间。一部分用来存储变量本身,另一部分用来存储数据,变量通过 标识(类似于指针) 指向数据。

那么 a, b = b, a 其实就是改变变量的指向而已:

image

节点实现

class Node:
    def __init__(self, elem):
        self.elem = elem        # 存储数据
        self.next = None        # 下一个节点标识

单链表操作

单链表实现

1、判断链表是否为空、链表长度、遍历链表

class SingleLinkList:
    def __init__(self, node=None):
        self.__head = node  # 表示一个节点
        
    def is_empty(self):
        """是否为空"""
        return self.__head = None
    
    def travel(self):
        """遍历所有元素"""
        cur = self.__head        # cur 初始时指向头节点
        while cur != None:
            print(cur.elem, end=' ')
            cur = cur.next      # 将cur后移一个节点
        print('')
        
    def length(self):
        """链表长度"""
        # cur 游标,用来移动遍历节点
        cur = self.__head
        count = 0   # 记录数量
        while cur != None:
            count += 1
            cur = cur.next
        return count
image

2、头部插入、尾部插入、任意位置插入:

头部插入 任意位置插入
def add(self, item):
    """头部插入,头插法(item:要插入的元素)"""
    node = Node(item)   # 创建一个节点以保存 item
    node.next = self.__head     # 新节点的 next 指向头节点,即 self.__head 指向的地方
    self.__head = node          # 将头节点 self._head  指向新节点
    
def append(self, item):
    """尾部插入,尾插法"""
    node = Node(item)
    # 链表是否为空,将 self.__head  指向新节点 
    if self.is_empty():
        self.__head = node
    # 不为空,则找到尾部,将尾节点的next指向新节点
    else:
        cur = self.__head
        while cur.next != None:     # 当 cur.next == None 时,说明已经找到了最后一个元素,那就跳出循环
            cur = cur.next
        cur.next = node     # 将尾节点的next指向新节点
        
def insert(self, pos, item):
    """pos:位置,item:要插入的元素,任意位置插入"""
    # 空链表,即插入在第一个,将 self.__head 指向新节点,也就是头插法
    if pos <= 0:
        self.add(item)
    elif pos > (self.length - 1):   # 尾插法
        self.append(item)
    else:                   # 中间插入
        pre = self.__head       # pre 用来指定 pos 的前一个位置 pos-1,初始从头开始移动到指定位置
        count = 0
        while count < (pos-1):
            count += 1
            pre = pre.next
        node = Node(item)
        node.next = pre.next        # 先将新节点 node 的 next 指向插入位置的节点(保证数据不丢失)
        pre.next = node     # 将插入位置的前一个节点的 next 节点指向新节点

3、删除节点

删除节点
    def remove(self, item):
        cur = self.__head
        pre = None
        while cur != None:
            # 找到指定元素
            if cur.elem == item:
                # 如果要删除的是第一个节点
                if cur == self.__head:
                    self.__head = cur.next      # 将头指针指向头节点的后一个节点
                else:
                    # 将删除位置的前一个节点的 next 指向删除位置的后一个节点
                    pre.next = cur.next
                    # cur.next = None
                break
            else:
                pre = cur
                cur = cur.next      # 继续移动节点

4、查找节点是否存在:

def search(self, item):
    """
        搜索、查找
        :return:
        """
    cur = self.__head
    while cur != None:
        if cur.elem == item:
            return True
        else:
            cur = cur.next
            return False

5、测试:

if __name__ == '__main__':
    s1 = SingleLinkList()
    print(s1.is_empty())
    print(s1.length())

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

    s1.append(2)
    s1.add(8)
    s1.append(3)
    s1.append(4)

    s1.insert(-1, 9)
    s1.travel()

    s1.insert(10, 200)
    s1.travel()

    s1.remove(100)
    s1.travel()

运行结果如下:

True
0
False
1
9 8 1 2 3 4 
9 8 1 2 3 4 200 
9 8 1 2 3 4 200 

链表与顺序表对比

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

操作 链表 顺序表
访问元素 O(n) O(1)
在头部插入/删除 O(1) O(n)
在尾部插入/删除 O(n) O(1)
在中间插入/删除 O(n) O(n)

3.2 双向链表

双向链表又称双链表,比之单链表多了一个前驱节点(前驱、后继节点)。

双向链表结构:

双向链表结构

操作

实现

class Node(object):
    def __init__(self, item):
        self.elem = item
        self.next = None
        self.pre = None


class DoubleLinkList(object):
    """
    is_empty()、append()、insert()、remove()、length()
    """

    def __init__(self, node=None):
        self.__head = node  # 头节点

    def is_empty(self):
        # if self.__head == None:
        #     return True
        return self.__head == None

    def length(self):
        cur = self.__head
        count = 0
        while cur != None:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        # 遍历
        cur = self.__head
        while cur != None:
            print(cur.elem, end=' ')
            cur = cur.next
        print('')

    def add(self, item):
        """
        头部添加
        :return:
        """
        node = Node(item)
        node.next = self.__head
        # self.__head.pre = node
        self.__head = node
        node.next.pre = node

    def append(self, item):
        # 尾部插入,尾插法
        node = Node(item)
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next != None:
                cur = cur.next
            cur.next = node
            node.pre = cur

    def insert(self, pos, item):
        if pos <= 0:
            self.add(item)
        elif pos > (self.length() - 1):
            self.append(item)
        else:
            cur = self.__head
            count = 0
            while count < pos:
                count += 1
                cur = cur.next
            node = Node(item)
            node.next = cur
            node.pre = cur.pre
            cur.pre.next = node
            cur.pre = node

    def remove(self, item):
        cur = self.__head
        while cur != None:
            if cur.elem == item:
                # 头节点
                if cur == self.__head:
                    self.__head = cur.next
                    # 只有一个节点
                    if cur.next:
                        cur.next.pre = None
                else:
                    cur.pre.next = cur.next
                    # 最后一个节点为的 next 指向 None,None 没有 pre 和 next
                    if cur.next:
                        cur.next.pre = cur.pre
                break
            else:
                cur = cur.next



    def search(self, item):
        """
        搜索、查找
        :return:
        """
        cur = self.__head
        while cur != None:
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        return False


if __name__ == '__main__':
    s1 = DoubleLinkList()
    print(s1.is_empty())
    print(s1.length())

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

    s1.append(2)    
    s1.add(8)
    s1.append(3)
    s1.append(4)    # 8 1 2 3 4 

    s1.insert(-1, 9)  # -1 当第一个处理,9 8 1 2 3 4 
    s1.travel()

    s1.insert(10, 200)   # 9 8 1 2 3 4 200
    s1.travel()

    s1.remove(200)      # 9 8 1 2 3 4 
    s1.travel()

3.3 单向循环链表

与单链表相比,单向循环链表最后一个节点不再指向 None,而是指向第一个节点,其结构如下:

单向循环链表结构

操作

操作

实现

1、是否为空、遍历即链表长度:

class Node(object):
    def __init__(self, item):
        self.elem = item
        self.next = None


class SingleCycleLinkList(object):
    """
    is_empty()、append()、insert()、remove()、length()
    """
    def __init__(self, node=None):
        self.__head = node        # 头节点

    def is_empty(self):
        # if self.__head == None:
        #     return True
        return self.__head == None

    def travel(self):
        # 遍历
        if self.is_empty():
            return
        cur = self.__head
        # cur.next = self.__head 表示是最后一个元素
        while cur.next != self.__head:
            print(cur.elem, end=' ')
            cur = cur.next
        # 最后一个元素没在循环中,单独取出
        print(cur.elem)

    def length(self):
        if self.is_empty():
            return 0
        cur = self.__head
        count = 1
        # cur.next = self.__head 表示是最后一个元素,所以 count 要从 1 开始,或者 return count+1
        while cur != self.__head:
            count += 1
            cur = cur.next
        return count

2、头插法、尾插法及任意位置插入:

任意位置插入
    def add(self, item):
        """
        头部添加
        :return:
        """
        node = Node(item)
        if self.is_empty():
            self.__head = node
            node.next = node  # 若链表为空,链表 next 指向自己
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            node.next = self.__head
            self.__head = node
            cur.next = self.__head  # 循环到链表最后一个元素,指向头节点

    def append(self, item):
        # 尾部插入,尾插法
        node = Node(item)
        if self.is_empty():
            self.__head = node
            node.next = node
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            node.next = self.__head
            cur.next = node

    def insert(self, pos, item):
        """pos 从 0 开始"""

        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
            node = Node(item)
            node.next = pre.next
            pre.next = node

3、搜索:

def search(self, item):
    """
        搜索、查找
        :return:
        """
    if self.is_empty():
        return False
    cur = self.__head
    while cur.next != self.__head:
        if cur.elem == item:
            return True
        else:
            cur = cur.next
            # 推出循环,cur 停留在最后一个节点上
            if cur.elem == item:
                # 若要找的元素恰好是最后一个元素
                return True
            return False

4、移除:

image
    def remove(self, item):
        if self.is_empty():
            return
        cur = self.__head
        pre = None
        while cur.next != self.__head:
            # 找到要移除的节点
            if cur.elem == item:
                # 要找的节点为头节点
                if cur == self.__head:
                    real = self.__head
                    # 循环找尾节点
                    while real.next != self.__head:
                        real = real.next
                    self.__head = cur.next
                    real.next = self.__head
                else:
                    pre.next = cur.next
                break
            else:
                # 继续往后移动
                pre = cur
                cur = cur.next

        # 退出循环没有找到节点,此时 cur 停留在最后一个节点上
        if cur.elem == item:
            if cur == self.__head:
                # 若只有一个节点元素
                self.__head = None
            else:
                pre.next = self.__head

测试

if __name__ == "__main__":
    ll = DLinkList()
    ll.add(1)
    ll.add(2)
    ll.append(3)
    ll.insert(2, 4)
    ll.insert(4, 5)
    ll.insert(0, 6)
    print "length:",ll.length()
    ll.travel()
    print ll.search(3)
    print ll.search(4)
    ll.remove(1)
    print "length:",ll.length()
    ll.travel()
上一篇 下一篇

猜你喜欢

热点阅读