算法学习【第三篇】:链表

 


链表#

1
2
3
4
5
6
7
为什么需要链表
顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以
使用起来并不是很灵活。
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
链表的定义
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是
在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)

单向链表#

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

1
2
3
·表元素域elem用来存放具体的数据。
·链接域next用来存放下一个节点的位置(python中的标识)
·变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。

节点实现#

1
2
3
4
5
6
7
class SingleNode(object):
  ““单链表的结点"
  def_init_(self,item):
    #_item存放数据元素
    self.item=item
    #_next是下一个节点的标识
    self.next=None

单链表的操作(其他链表大致相同)#

1
2
3
4
5
6
7
8
·is_empty0链表是否为空
·length0链表长度
·travel0遍历整个链表
·add(item)链表头部添加元素
·append(item)链表尾部添加元素
·insert(pos,item)指定位置添加元素
·remove(item)删除节点
·search(item)查找节点是否存在

单链表的实现#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# coding:utf-8
 
 
class Node(object):
    """节点Node"""
 
    def __init__(self, elem):
        self.elem = elem
        self.next = None
 
 
class SingleLinkList(object):
    """单链表"""
 
    def __init__(self, node=None):
        self.__head = None
 
    def is_empty(self):
        '''链表是否为空'''
        return self.__head == None
 
    def length(self):
        '''链表长度'''
        # cur游标,用来移动遍历节点
        cur = self.__head
        # count记录数量
        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):
        '''链表头部添加元素,头插法'''
        node = Node(item)
        node.next = self.__head
        self.__head = 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
 
    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
            # 当循环退出后,pre指向pos-1位置
            node = Node(item)
            node.next = pre.next
            pre.next = node
 
    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:
                    pre.next = cur.next
                break
            else:
                pre = cur
                cur = cur.next
 
    def search(self, item):
        '''查找节点是否存在'''
        cur = self.__head
        while cur != None:
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        return False
 
 
if __name__ == '__main__':
    l1 = SingleLinkList()
    print(l1.is_empty())
    print(l1.length())
 
    l1.append(1)
    print(l1.is_empty())
    print(l1.length())
 
    l1.append(2)
    l1.add(8)
    l1.append(3)
    l1.append(4)
    l1.append(5)
    l1.append(6)
    l1.insert(-1, 9)
    l1.travel()
    l1.insert(3, 100)
    l1.travel()
    l1.insert(10, 200)
    l1.travel()
 
 
    l1.remove(100)
    l1.travel()
 
    l1.remove(9)
    l1.travel()
    l1.remove(200)
    l1.travel()
 
'''
True
0
False
1
9 8 1 2 3 4 5 6
9 8 1 100 2 3 4 5 6
9 8 1 100 2 3 4 5 6 200
9 8 1 2 3 4 5 6 200
8 1 2 3 4 5 6 200
8 1 2 3 4 5 6
''' 

单向循环链表#

单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# coding:utf-8
 
 
class Node(object):
    """结点Node"""
 
    def __init__(self, elem):
        self.elem = elem
        self.next = None
        self.prev = None
 
 
class DoubuleLinkList(object):
    """单向循环链表"""
 
    def __init__(self, node=None):
        self.__head = node
        if node:
            node.next = node
 
    def is_empty(self):
        '''链表是否为空'''
        return self.__head == None
 
    def length(self):
        '''链表长度'''
        if self.is_empty():
            return 0
        # cur游标,用来移动遍历节点
        cur = self.__head
        # count记录数量
        count = 1
        while cur.next != self.__head:
            count += 1
            cur = cur.next
        return count
 
    def travel(self):
        '''遍历整个链表'''
        if self.is_empty():
            return
        cur = self.__head
        while cur.next != self.__head:
            print(cur.elem, end=" ")
            cur = cur.next
        # 退出循环,cur指向尾结点,但尾结点的元素未打印
        print(cur.elem)
 
    def add(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
            # 退出循环,cur指向尾结点
            node.next = self.__head
            self.__head = node
            # cur.next = 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=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
                # 当前循环退出后,pre指向pos-1位置
            node = Node(item)
            node.next = pre.next
            pre.next = node
 
    def remove(self, item):
        '''删除节点'''
 
        cur = self.__head
        pre = None
 
        while cur.next != self.__head:
            if cur.elem == item:
                # 先判断是否是头节点
                if cur == self.__head:
                    # 头结点情况
                    # 找尾结点
                    rear = self.__head
                    while rear.next != self.__head:
                        rear = rear.next
                    self.__head = cur.next
                    rear.next = self.__head
                else:
                    # 中间结点
                    pre.next = cur.next
                return
            else:
                pre = cur
                cur = cur.next
        # 退出循环,cur指向尾结点
        if cur.elem == item:
            if cur == self.__head:
                # 链表只有一个结点
                self.__head = None
            else:
                # pre.next=cur.next
                pre.next = self.__head
 
    def search(self, item):
        '''查找节点是否存在'''
        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
 
 
if __name__ == '__main__':
    l1 = DoubuleLinkList()
    print(l1.is_empty())
    print(l1.length())
 
    l1.append(1)
    print(l1.is_empty())
    print(l1.length())
 
    l1.append(2)
    l1.add(8)
    l1.append(3)
    l1.append(4)
    l1.append(5)
    l1.append(6)
    l1.insert(-1, 9)
    l1.travel()
    l1.insert(3, 100)
    l1.travel()
    l1.insert(10, 200)
    l1.travel()
 
    l1.remove(100)
    l1.travel()
 
    l1.remove(9)
    l1.travel()
    l1.remove(200)
    l1.travel()
 
'''
True
0
False
1
9 8 1 2 3 4 5 6
9 8 1 100 2 3 4 5 6
9 8 1 100 2 3 4 5 6 200
9 8 1 2 3 4 5 6 200
8 1 2 3 4 5 6 200
8 1 2 3 4 5 6
'''

双向链表#

一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个链接:一个指向前一个节点,当此节点为第
一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# coding:utf-8
 
 
class Node(object):
    """结点Node"""
 
    def __init__(self, elem):
        self.elem = elem
        self.next = None
        self.prev=None
 
 
class DoubuleLinkList(object):
    """双链表"""
 
    def __init__(self, node=None):
        self.__head = None
 
    def is_empty(self):
        '''链表是否为空'''
        return self.__head == None
 
    def length(self):
        '''链表长度'''
        # cur游标,用来移动遍历节点
        cur = self.__head
        # count记录数量
        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):
        '''链表头部添加元素,头插法'''
        node = Node(item)
        node.next = self.__head
        self.__head = node
        node.next.prev=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.prev=cur
 
    def insert(self, pos, item):
        '''指定位置添加元素
            pos  从0开始
        '''
        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
            # 当循环退出后,cur指向pos位置
            node = Node(item)
            node.next=cur
            node.prev=cur.prev
            cur.prev.next=node
            cur.prev=node
 
 
    def remove(self, item):
        '''删除节点'''
        cur = self.__head
 
        while cur != None:
            if cur.elem == item:
                # 先判断是否是头节点
                # 头节点
                if cur == self.__head:
                    if cur.next:
                        #判断链表是否只有一个结点
                        cur.next.prev=None
                else:
                    cur.prev.next=cur.next
                    if cur.next:
                        cur.next.prev = cur.prev
                break
 
            else:
                cur = cur.next
 
    def search(self, item):
        '''查找节点是否存在'''
        cur = self.__head
        while cur != None:
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        return False
 
 
if __name__ == '__main__':
    l1 = DoubuleLinkList()
    print(l1.is_empty())
    print(l1.length())
 
    l1.append(1)
    print(l1.is_empty())
    print(l1.length())
 
    l1.append(2)
    l1.add(8)
    l1.append(3)
    l1.append(4)
    l1.append(5)
    l1.append(6)
    l1.insert(-1, 9)
    l1.travel()
    l1.insert(3, 100)
    l1.travel()
    l1.insert(10, 200)
    l1.travel()
 
 
    l1.remove(100)
    l1.travel()
 
    l1.remove(9)
    l1.travel()
    l1.remove(200)
    l1.travel()
 
'''
True
0
False
1
9 8 1 2 3 4 5 6
9 8 1 100 2 3 4 5 6
9 8 1 100 2 3 4 5 6 200
9 8 1 2 3 4 5 6 200
8 1 2 3 4 5 6 200
8 1 2 3 4 5 6
'''

双向循环链表#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# 链表的节点
class Node(object):
    def __init__(self, item):
        self.item = item  # 节点数值
        self.prev = None  # 用于指向前一个元素
        self.next = None  # 用于指向后一个元素
 
# 双向循环链表
class DoubleCircleLinkList(object):
    def __init__(self):
        self.__head = None  # 初始化的时候头节点设为空、
 
    # 判断链表是否为空,head为None 的话则链表是空的
    def is_empty(self):
        return self.__head is None
 
    # 头部添加元素的方法
    def add(self, item):
        node = Node(item)  # 新建一个节点node 里面的值是item
        # 如果链表是空的,则node的next和prev都指向自己(因为是双向循环),head指向node
        if self.is_empty():
            self.__head = node
            node.next = node
            node.prev = node
        # 否则链表不空
        else:
            node.next = self.__head  # node的next设为现在的head
            node.prev = self.__head.prev  # node的prev 设为现在head的prev
            self.__head.prev.next = node  # 现在head的前一个元素的next设为node
            self.__head.prev = node  # 现在head的前驱 改为node
            self.__head = node  # 更改头部指针
 
    # 尾部添加元素方法
    def append(self, item):
        # 如果当前链表是空的 那就调用头部插入方法
        if self.is_empty():
            self.add(item)
        # 否则链表不为空
        else:
            node = Node(item)  # 新建一个节点node
            # 因为是双向循环链表,所以head的prev其实就是链表的尾部
            node.next = self.__head  # node的下一个设为头
            node.prev = self.__head.prev  # node的前驱设为现在头部的前驱
            self.__head.prev.next = node  # 头部前驱的后继设为node
            self.__head.prev = node  # 头部自己的前驱改为node
 
    # 获得链表长度 节点个数
    def length(self):
        # 如果链表是空的 就返回0
        if self.is_empty():
            return 0
        # 如果不是空的
        else:
            cur = self.__head  # 临时变量cur表示当前位置 初始化设为头head
            count = 1  # 设一个计数器count,cur每指向一个节点,count就自增1  目前cur指向头,所以count初始化为1
            # 如果cur.next不是head,说明cur目前不是最后一个元素,那么count就1,再让cur后移一位
            while cur.next is not self.__head:
                count += 1
                cur = cur.next
            # 跳出循环说明所有元素都被累加了一次 返回count就是一共有多少个元素
            return count
 
    # 遍历链表的功能
    def travel(self):
        # 如果当前自己是空的,那就不遍历
        if self.is_empty():
            return
        # 链表不空
        else:
            cur = self.__head  # 临时变量cur表示当前位置,初始化为链表的头部
            # 只要cur的后继不是头说明cur不是最后一个节点,我们就输出当前值,并让cur后移一个节点
            while cur.next is not self.__head:
                print(cur.item, end=" ")
                cur = cur.next
            # 当cur的后继是head的时候跳出循环了,最后一个节点还没有打印值 在这里打印出来
            print(cur.item)
 
    # 置顶位置插入节点
    def insert(self, pos, item):
        # 如果位置<=0 则调用头部插入方法
        if pos <= 0:
            self.add(item)
        # 如果位置是最后一个或者更大 就调用尾部插入方法
        elif pos > self.length() - 1:
            self.append(item)
        # 否则插入位置就是链表中间
        else:
            index = 0  # 设置计数器,用于标记我们后移了多少步
            cur = self.__head  # cur标记当前所在位置
            # 让index每次自增1 ,cur后移,当index=pos-1的时候说明cur在要插入位置的前一个元素,这时候停下
            while index < pos - 1:
                index += 1
                cur = cur.next
            # 跳出循环,cur在要插入位置的前一个元素,将node插入到cur的后面
            node = Node(item)  # 新建一个节点
            node.next = cur.next  # node的后继设为cur的后继
            node.prev = cur  # node的前驱设为cur
            cur.next.prev = node  # cur后继的前驱改为node
            cur.next = node  # cur后继改为node
 
    # 删除节点操作
    def remove(self, item):
        # 如果链表为空 直接不操作
        if self.is_empty():
            return
        # 链表不为空
        else:
            cur = self.__head  # 临时变量标记位置,从头开始
            # 如果头结点就是 要删除的元素
            if cur.item == item:
                # 如果只有一个节点 链表就空了 head设为None
                if self.length() == 1:
                    self.__head = None
                # 如果多个元素
                else:
                    self.__head = cur.next  # 头指针指向cur的下一个
                    cur.next.prev = cur.prev  # cur后继的前驱改为cur的前驱
                    cur.prev.next = cur.next  # cur前驱的后继改为cur的后继
            # 否则 头节点不是要删除的节点 我们要向下遍历
            else:
                cur = cur.next  # 把cur后移一个节点
                # 循环让cur后移一直到链表尾元素位置,期间如果找得到就删除节点,找不到就跳出循环,
                while cur is not self.__head:
                    # 找到了元素cur就是要删除的
                    if cur.item == item:
                        cur.prev.next = cur.next  # cur的前驱的后继改为cur的后继
                        cur.next.prev = cur.prev  # cur的后继的前驱改为cur的前驱
                    cur = cur.next
 
    # 搜索节点是否存在
    def search(self, item):
        # 如果链表是空的一定不存在
        if self.is_empty():
            return False
        # 否则链表不空
        else:
            cur = self.__head  # 设置临时cur从头开始
            # cur不断后移,一直到尾节点为止
            while cur.next is not self.__head:
                # 如果期间找到了就返回一个True 结束运行
                if cur.item == item:
                    return True
                cur = cur.next
            # 从循环跳出来cur就指向了尾元素 看一下为元素是不是要找的 是就返回True
            if cur.item == item:
                return True
            # 所有元素都不是 就返回False 没找到
            return False

链表与顺序表的对比#

链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的
使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:

1
2
注意虽然表面看起来复杂度都是O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前
后移位操作,只能通过拷贝和覆盖的方法进行。
posted @   鲸鱼的海老大  阅读(205)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示
CONTENTS