一、顺序表
1、顺序表的结构
元素集合,另一部分是为实现正确操作而需记录的信息,即有关表的整体情况的信息,这部分信息主要包括元素存储区的容量和当前表中已有的元素个数两项。
2、顺序表的两种基本实现方式
一体式结构,存储表信息的单元与元素存储区以连续的方式安排在一块存储区里,两部分数据的整体形成一个完整的顺序表对象。一体式结构整体性强,易于管理。但是由于数据元素存储区域是表对象的一部分,顺序表创建后,元素存储区就固定了。
分离式结构,表对象里只保存与整个表有关的信息(容量和元素个数),实际数据元素存放在另一个独立的元素存储区里,通过链接与基本表对象关联。
3、元素存储区替换
一体式结构由于顺序表信息区与数据区联系存储在一起,所以若想更换数据区,则只能整体搬迁,即整个顺序表对象(指存储顺序表的结构信息的区域)改变了。
分离式结构若想更换数据区,只需将表信息区中的数据区链接地址更新即可,而该顺序表对象不变。
4、元素存储区扩充及其策略
动态顺序表,因为其容量可以在使用中动态变化。
扩充的两种策略:
固定数目的存储位置,如每次扩充10个元素位置,这种策略可称为线性增长。
(特点:节省空间,但是扩充操作频繁,操作次数多)
容量加倍,如每次扩充增加一倍存储空间。
(特点:减少了扩充操作的执行次数,但可能会浪费空间资源。以空间换时间,推荐的方式)
list 实现采用了如下的策略:在建立空表(或很小的表)时,系统分配一块能容纳8个元素的存储区;在执行插入操作(insert或append)时,如果元素存储区满就换一块4倍大的存储区。但如果此时的表已经很大(目前阀值为50000),则改变策略,采用加一倍的方法。引入这种改变策略的方式,是为了避免出现过多的空闲的存储位置。
5、顺序表的操作
增加元素,下图为顺序表增加元素的三种方式:
a、尾端加入元素,时间复杂度为 O(1)
b、非保序的加入元素(不常见)没时间复杂度为 O(1)
c、保序的元素加入,时间复杂度为 O(n)
删除元素,下图为顺序表删除元素的三种方式:
a、删除表尾元素,时间复杂度为 O(1)
b、非保序的元素删除(不常见),时间复杂度为 O(1)
c、保序的元素删除,时间复杂度为 O(n)
6、Python 中的顺序表
list 和 tuple 两种类型采用了顺序表的实现技术,具有前面讨论的顺序表的所有性质。
tuple是不可变类型,即不变的顺序表,因此不支持改变其内部状态任何操作,而其他方面,则与list的性质类似。
list的基本实现技术:
Python表中类型list就是一种元素个数可变的线性表,可以加入和删除元素,并在各种操作维持已有元素顺序(即保序),而且还具有以下行为特征:
》基于下标(位置)的高效元素访问和更新,时间复杂度应该是 O(1);
采用顺序表技术,表中元素保存在一块连续的存储区中。
》允许任意加入元素,而且在不断加入元素的过程中,表对象的标识(函数id得到的值)不变
采用分离式实现技术。
list就是一种采用分离式技术实现的动态顺序表。这就是为什么用list.append(x)(或list.insert(len(list), x), 即尾部插入)比在指定位置插入元素效率高的原因。
二、链表
相对于顺序表,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理,因为顺序表的结构需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁。
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是每一个结点(数据存储单元)里存放下一个结点的信息(即地址):
1、单向链表
单向链表也叫单链表,是表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。
表中元素elem用来存放具体的数据。
链接域next用来存放下一个节点的位置(Python中的标识)。
变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
单链表的操作:
is_empty():链表是否为空
length():链表长度
travel():遍历整个链表
add(item):链表头部添加元素
append(item):链表尾部添加元素
insert(pos, item):指定位置添加元素
remove(item):删除节点
search(item):查找节点是否存在
代码实现:
1 # coding=utf-8
2 # 单链表的实现
3
4
5 class SingleNode:
6 """单链表的节点"""
7 def __init__(self, item):
8 # item存放数据元素
9 self.item = item
10 # 下一个节点
11 self.next = None
12
13 def __str__(self):
14 return str(self.item)
15
16
17 class SingleLinkList:
18 """单链表"""
19 def __init__(self):
20 self._head = None
21
22 def is_empty(self):
23 """判断链表是否为空"""
24 return self._head is None
25
26 def length(self):
27 """获取链表长度"""
28 cur = self._head
29 count = 0
30 while cur is not None:
31 count += 1
32 # 将cur后移,指向下一个节点
33 cur = cur.next
34 return count
35
36 def travel(self):
37 """遍历链表"""
38 cur = self._head
39 while cur is not None:
40 print(cur.item)
41 cur = cur.next
42 print("")
43
44 def add(self, item):
45 """链表头部添加元素"""
46 node = SingleNode(item)
47
48 node.next = self._head
49 self._head = node
50
51 def append(self, item):
52 """链表尾部添加元素"""
53 node = SingleNode(item)
54
55 if self.is_empty():
56 self._head = node
57 else:
58 cur = self._head
59 while cur.next is not None:
60 cur = cur.next
61
62 # 此时cur指向链表最后一个节点,即 next = None
63 cur.next = node
64
65 def insert(self, pos, item):
66 """指定位置添加元素"""
67 # 若指定位置pos为第一个元素之前,则执行头部插入
68 if pos <= 0:
69 self.add(item)
70
71 # 若指定位置超过链表尾部,则执行尾部插入
72 elif pos > (self.length() - 1):
73 self.append(item)
74
75 # 找到指定位置
76 else:
77 node = SingleNode(item)
78 cur = self._head
79 cur_pos = 0
80 while cur.next is not None:
81 # 获取需要插入位置的上一个节点
82 if pos - 1 == cur_pos:
83 node.next = cur.next
84 cur.next = node
85 cur = cur.next
86 cur_pos += 1
87
88 def remove(self, item):
89 """删除节点"""
90 cur = self._head
91 while cur is not None:
92 if cur.next.item == item:
93 cur.next = cur.next.next
94 break
95 cur = cur.next
96
97 def search(self, item):
98 """查找节点是否存在"""
99 cur = self._head
100 count = 0
101 while cur is not None:
102 if cur.item == item:
103 return count
104 cur = cur.next
105 count += 1
106
107 # 找不到元素
108 if count == self.length():
109 count = -1
110 return count
111
112
113 if __name__ == "__main__":
114 ll = SingleLinkList()
115 ll.add(1) # 1
116 ll.add(2) # 2 1
117 ll.append(3) # 2 1 3
118 ll.insert(2, 4) # 2 1 4 3
119 print("length:", ll.length()) # 4
120 ll.travel() # 2 1 4 3
121 print("search(3):", ll.search(3)) # 3
122 print("search(5):", ll.search(5)) # -1
123 ll.remove(1)
124 print("length:", ll.length()) # 3
125 ll.travel() # 2 4 3
链表与顺序表的对比:
链表失去了顺序表随机读取的优点,同时链表由于增加了节点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:
操作 | 链表 | 顺序表 |
访问元素 | O(n) | O(1) |
在头部插入/删除 | O(1) | O(n) |
在尾部安插入/删除 | O(n) | O(1) |
在中间插入/删除 | O(n) | O(n) |
注意:虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后所有元素进行前后移位操作,只能通过拷贝和覆盖方法进行。
2、单向循环链表
单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头结点。
基本操作和单链表基本一样,实现代码如下:
1 # coding=utf-8
2 # 单向循环链表
3
4
5 class Node:
6 """节点"""
7 def __init__(self, item):
8 self.item = item
9 self.next = None
10
11 def __str__(self):
12 return str(self.item)
13
14
15 class SinCycLinkedList:
16 """单向循环链表"""
17 def __init__(self):
18 self._head = None
19
20 def is_empty(self):
21 """判断链表是否为空"""
22 return self._head is None
23
24 def length(self):
25 """链表长度"""
26 if self.is_empty():
27 return 0
28 count = 1
29 cur = self._head
30 while cur.next != self._head:
31 # print("cur", cur.item)
32 count += 1
33 cur = cur.next
34 return count
35
36 def travel(self):
37 """遍历"""
38 if self.is_empty():
39 return
40
41 cur = self._head
42 print(cur.item)
43 while cur.next != self._head:
44 cur = cur.next
45 print(cur.item)
46
47 def add(self, item):
48 """在头部添加一个节点"""
49 node = Node(item)
50 if self.is_empty():
51 self._head = node
52 node.next = self._head
53 else:
54 node.next = self._head
55 cur = self._head
56 while cur.next != self._head:
57 cur = cur.next
58
59 cur.next = node
60 self._head = node
61
62 def append(self, item):
63 """在尾部添加一个节点"""
64 node = Node(item)
65 if self.is_empty():
66 self._head = node
67 node.next = self._head
68 else:
69 cur = self._head
70 # print(type(cur), cur.item, cur.next)
71 while cur.next != self._head:
72 cur = cur.next
73
74 # print(cur.item)
75 cur.next = node
76 node.next = self._head
77
78 def insert(self, pos, item):
79 """指定位置pos添加节点"""
80 if pos <= 0:
81 self.add(item)
82 elif pos > (self.length() - 1):
83 self.append(item)
84 else:
85 node = Node(item)
86 cur = self._head
87 cur_pos = 0
88 while cur.next != self._head:
89 if (pos - 1) == cur_pos:
90 node.next = cur.next
91 cur.next = node
92 break
93 cur_pos += 1
94 cur = cur.next
95
96 def remove(self, item):
97 """删除一个节点"""
98 if self.is_empty():
99 return
100
101 pre = self._head
102 # 删除首节点
103 if pre.item == item:
104 cur = pre
105 while cur.next != self._head:
106 cur = cur.next
107
108 cur.next = pre.next # 删除首节点(跳过该节点)
109 self._head = pre.next # 重新指定首节点
110
111 # 删除其他的节点
112 else:
113 cur = pre
114 while cur.next != self._head:
115 if cur.next.item == item:
116 cur.next = cur.next.next
117 cur = cur.next
118
119 def search(self, item):
120 """查找节点是否存在"""
121 if self.is_empty():
122 return -1
123
124 cur_pos = 0
125 cur = self._head
126 if cur.item == item:
127 return cur_pos
128
129 while cur.next != self._head:
130 if cur.item == item:
131 return cur_pos
132 cur_pos += 1
133 cur = cur.next
134
135 if cur_pos == self.length() - 1:
136 return -1
137
138
139 if __name__ == "__main__":
140 ll = SinCycLinkedList()
141 ll.add(1) # 1
142 ll.add(2) # 2 1
143 # ll.travel()
144 ll.append(3) # 2 1 3
145 ll.insert(2, 4) # 2 1 4 3
146 ll.insert(4, 5) # 2 1 4 3 5
147 ll.insert(0, 6) # 6 2 1 4 3 5
148 print("length:", ll.length()) # 6
149 ll.travel() # 6 2 1 4 3 5
150 print("search(3)", ll.search(3)) # 4
151 print("search(7)", ll.search(7)) # -1
152 print("search(6)", ll.search(6)) # 0
153 print("remove(1)")
154 ll.remove(1)
155 print("length:", ll.length()) # 6 2 4 3 5
156 print("remove(6)")
157 ll.remove(6)
158 ll.travel()
3、双向链表
一种更复杂的链表是 "双向链表" 或 "双面链表"。每个节点有两个链接:一个指向前一个节点,当次节点为第一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
基本操作和单链表一样,不同的实现,代码如下:
1 # coding=utf-8
2 # 双向链表
3
4
5 class Node:
6 """节点"""
7 def __init__(self, item):
8 self.item = item
9 self.prev = None
10 self.next = None
11
12
13 class DLinkList:
14 """双向链表"""
15 def __init__(self):
16 self._head = None
17
18 def is_empty(self):
19 """判断链表是否为空"""
20 return self._head is None
21
22 def length(self):
23 """获取链表长度"""
24 if self.is_empty():
25 return 0
26 else:
27 cur = self._head
28 count = 1
29 while cur.next is not None:
30 count += 1
31 cur = cur.next
32
33 return count
34
35 def travel(self):
36 """遍历链表"""
37 print("↓↓" * 10)
38 if self.is_empty():
39 print("")
40
41 else:
42 cur = self._head
43 print(cur.item)
44 while cur.next is not None:
45 cur = cur.next
46 print(cur.item)
47 print("↑↑" * 10)
48
49 def add(self, item):
50 """链表头部添加节点"""
51 node = Node(item)
52 if self.is_empty():
53 self._head = node
54 else:
55 cur = self._head
56
57 node.next = cur
58 cur.prev = node
59 self._head = node
60
61 def append(self, item):
62 """链表尾部添加节点"""
63 node = Node(item)
64 if self.is_empty():
65 self._head = node
66 else:
67 cur = self._head
68 # 遍历找到最后一个节点
69 while cur.next is not None:
70 cur = cur.next
71
72 # 在尾节点添加新的节点
73 cur.next = node
74 node.prev = cur
75
76 def insert(self, pos, item):
77 """指定位置添加"""
78 # 头部添加
79 if pos <= 0:
80 self.add(item)
81
82 # 尾部添加
83 elif pos > (self.length() - 1):
84 self.append(item)
85
86 # 其他位置添加
87 else:
88 node = Node(item)
89
90 cur = self._head
91 cur_pos = 0
92 while cur.next is not None:
93 if cur_pos == (pos - 1):
94 # 与下一个节点互相指向
95 node.next = cur.next
96 cur.next.prev = node
97 # 与上一个节点互相指向
98 cur.next = node
99 node.prev = cur
100 cur_pos += 1
101 cur = cur.next
102
103 def remove(self, item):
104 """删除节点"""
105 if self.is_empty():
106 return
107 else:
108 cur = self._head
109 # 删除首节点
110 if cur.item == item:
111 self._head = cur.next
112 cur.next.prev = None
113
114 # 删除其他节点
115 else:
116 while cur.next is not None:
117 if cur.item == item:
118 # 删除之前:1 ←→ [2] ←→ 3
119 # 删除之后:1 ←→ 3
120 cur.prev.next = cur.next
121 cur.next.prev = cur.prev
122 cur = cur.next
123
124 # 删除尾节点
125 if cur.item == item:
126 cur.prev.next = None
127
128
129 def search(self, item):
130 """查找节点是否存在"""
131 if self.is_empty():
132 return -1
133 else:
134 cur = self._head
135 cur_pos = 0
136 while cur.next is not None:
137 if cur.item == item:
138 return cur_pos
139
140 cur_pos += 1
141 cur = cur.next
142
143 if cur_pos == (self.length() - 1):
144 return -1
145
146
147 if __name__ == "__main__":
148 ll = DLinkList()
149 ll.add(1) # 1
150 ll.add(2) # 2 1
151 ll.append(3) # 2 1 3
152 ll.insert(2, 4) # 2 1 4 3
153 ll.insert(4, 5) # 2 1 4 3 5
154 ll.insert(0, 6) # 6 2 1 4 3 5
155 print("length:", ll.length()) # 6
156 ll.travel() # 6 2 1 4 3 5
157 print("search(3)", ll.search(3))
158 print("search(4)", ll.search(4))
159 print("search(10)", ll.search(10))
160 ll.remove(1)
161 print("length:", ll.length())
162 ll.travel()
163 print("删除首节点 remove(6):")
164 ll.remove(6)
165 ll.travel()
166 print("删除尾节点 remove(5):")
167 ll.remove(5)
168 ll.travel()
三、栈
栈(stack),也称为堆栈,是一种容器,可存入数据元素、访问元素、删除元素,它的特点在于只能允许在容器的一端(称为栈顶端指标:top)进行加入数据(push)和输出数据(pop)的运算。没有了位置概念,保证任何时候可以访问、删除的元素都是此前最后存入的那个元素,确定了一种默认的访问顺序。
由于栈数据结构只允许在一端进行操作,因为按照后进先出(LIFO,Last In First Out)的原理运作。
栈可以用顺序表实现,也可以用链表实现。
1、栈的操作:
Stack():创建一个新的空栈
push(item):添加一个新的元素item到栈顶
pop():弹出栈顶元素
peek():返回栈顶元素
is_empty():判断栈是否为空
size():返回栈的元素个数
2、代码实现
1 # coding=utf-8
2
3
4 class Stack:
5 """栈"""
6 def __init__(self):
7 self.items = []
8
9 def is_empty(self):
10 """判断是否为空"""
11 return self.items == []
12
13 def push(self, item):
14 """加入元素"""
15 self.items.append(item)
16
17 def pop(self):
18 """弹出元素"""
19 return self.items.pop()
20
21 def peek(self):
22 """返回栈顶元素"""
23 return self.items[len(self.items) - 1]
24
25 def size(self):
26 """返回栈的元素个数"""
27 return len(self.items)
28
29
30 if __name__ == "__main__":
31 stack = Stack()
32 stack.push("hello")
33 stack.push("world")
34 stack.push("stack")
35 print(stack.size()) # 3
36 print(stack.peek()) # stack
37 print(stack.pop()) # stack
38 print(stack.pop()) # world
39 print(stack.pop()) # hello
四、队列
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出(FIFO,First In First Out)的线性表。允许插入的一端为队尾,允许删除的一端为队头。队列不允许在中间部位进行操作!假设队列 q=(a1, a2, ,..., an),那么a1就是队头元素,而an是队尾元素。这样在删除时,总是从a1开始,而插入时,总是在队列最后。
1、队列的实现(同栈一样,队列也可以用顺序表或者链表实现):
队列的操作:
Queue():创建一个空的队列
enqueue(item):往队列中添加一个item元素
dequeue():从队列头部删除一个元素
is_empty():判断一个队列是否为空
size():返回队列的大小
1 # coding=utf-8
2
3
4 class Queue:
5 """队列"""
6 def __init__(self):
7 self.items = []
8
9 def is_empty(self):
10 return self.items == []
11
12 def enqueue(self, item):
13 """添加元素"""
14 self.items.insert(0, item)
15
16 def dequeue(self):
17 """从队列头部删除一个元素"""
18 return self.items.pop()
19
20 def size(self):
21 return len(self.items)
22
23
24 if __name__ == "__main__":
25 q = Queue()
26 q.enqueue("hello")
27 q.enqueue("world")
28 q.enqueue("queue")
29 print(q.size())
30 print(q.dequeue()) # hello
31 print(q.dequeue()) # world
32 print(q.dequeue()) # queue
2、双端队列的实现
双端队列(deque,全名 double-ended queue),是一种具有队列和栈的性质的数据结构。
双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。双端队列可以在队列任意一端入队和出队。
操作:
Deque():创建一个空的双端队列
add_front(item):从队头加入一个item元素
add_rear(item):从队尾加入一个item元素
remove_front():从队头删除一个元素
remove_rear():从队尾删除一个元素
is_empty():判断双端队列是否为空
size():返回队列的大小
1 # coding=utf-8
2
3
4 class Deque:
5 """双端队列"""
6 def __init__(self):
7 self.items = []
8
9 def add_front(self, item):
10 """从队头加入一个元素"""
11 self.items.insert(0, item)
12
13 def add_rear(self, item):
14 """从队尾加入一个元素"""
15 self.items.append(item)
16
17 def remove_front(self):
18 """从队头删除一个元素"""
19 return self.items.pop(0)
20
21 def remove_rear(self):
22 """从队尾删除一个元素"""
23 return self.items.pop()
24
25 def is_empty(self):
26 """是否为空"""
27 return self.items == []
28
29 def size(self):
30 """队列长度"""
31 return len(self.items)
32
33
34 if __name__ == "__main__":
35 deque = Deque()
36 deque.add_front(1)
37 deque.add_front(2)
38 deque.add_rear(3)
39 deque.add_rear(4)
40 print(deque.size()) # 4
41 print(deque.remove_front()) # 2
42 print(deque.remove_front()) # 1
43 print(deque.remove_rear()) # 4
44 print(deque.remove_rear()) # 3