前置疑问
Q1 C语言中实现单链表需要用到结构体,python中如何实现?
Q2 面向过程和面向对象实现一个单链表到底有什么不同的感受?
学习内容
1、定义单链表
2、单链表的实现
3、单链表的方法
4、单链表和顺序表的区别
学习时突发疑问
Q3 python中实现链表的时候,为什么要定义两个类Node一个SingleList
Q4 current和current.next 分别代表什么?
Q5 什么时候用current?什么时候用current.next?
学习产出
1、定义单链表
- 单链表是线性表,一个单链表是由一个一个节点构成,每个节点有两个域,一个数据域,一个指针域。
- “链”表示是一个接着一个,如何实现这种表示,前面一个节点指针域存放后一个节点的地址。谁在谁前面就是前驱,谁在谁后面就是后继,头节点不存在前驱,尾结点不存在后继。
2、单链表的实现
A1
- 节点的表示
用C语言面向过程表示,可以用typedef + struct 来表示一个节点如下:
typedef struct node
{
ElemType data;
struct node *next;
}Node, *LinkList;
但是刚学完python如何实现这个节点呢?用类,造一个出来
class Node:
def __init__(self, data):
self.data = data
self.next = None
- 链表的表示
C语言中用如下方法表示创建好了一个链表L
LinkList L = (LinkList)malloc(sizeof(Node));
L->next = NULL
class SingleList:
"""实现单链表"""
def __init__(self, node=None):
self._head = node
my_list = SingleList()
图片由python Tutor生成
A3
为什么要创建两个类呢?因为最终的操作是只用链表这个对象去操作里面的节点,节点是一个对象,需要一个类,链表也是个对象,也需要个类。
3、单链表的常规操作
- is_empty
'''判断链表是否为空'''
def is_empty(self):
return self._head is None
- length
def length(self):
current = self._head
count = 0
while current is not None:
count += 1
current = current.next
return count
- travel
'''遍历链表'''
def travel(self):
current = self._head
while current is not None:
print("{0}".format(current.data), end=" ")
current = current.next
print(" ")
- add
'''在链表头部插入节点,头插法'''
def add(self, item):
node = Node(item)
if self.is_empty():
self._head = node
else:
node.next = self._head
self._head = node
- append
'''在链表尾部插入元素,尾插法'''
def append(self, item):
node = Node(item)
if self.is_empty():
self._head = node
else:
current = self._head
while current.next is not None:
current = current.next
current.next = node
6.insert
'''
在某个位置插入链表
两个特殊位置
1、在0位置 add
2、在最后一个位置 append
在插入一个节点的时候需要考虑当前位置的前一个节点。
'''
def insert(self, item, pos):
if pos <= 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
node = Node(item)
pre = self._head
count = 0
while count < (pos - 1):
pre = pre.next
count += 1
node.next = pre.next
pre.next = node
- remove
'''
删除节点
需要注意如果删除的节点就是第一个怎么操作?
'''
def remove(self, item):
current = self._head
if self.is_empty():
return
elif current.data == item:
self._head = current.next
else:
previous = None
while current is not None and current.data != item:
previous = current
current = current.next
previous.next = current.next
- search
'''查找链表中的节点'''
def search(self, item):
found = False
if self.is_empty():
return found
else:
current = self._head
while current is not None and not found:
if current.data == item:
found = True
else:
current = current.next
return found
8.实现上面操作
if __name__ == "__main__":
my_list = SingleList()
print("add操作:")
my_list.add(98)
my_list.add(99)
my_list.travel()
print("{:#^50}".format(""))
print("append操作:")
my_list.append(100)
my_list.append(101)
my_list.append(102)
my_list.append(103)
my_list.travel()
print("{:#^50}".format(""))
print("insert操作:")
my_list.insert(99, 0)
my_list.insert(104, 10)
my_list.insert(19, 3)
my_list.insert(56, 5)
my_list.travel()
print("{:#^50}".format(""))
print("remove操作:")
my_list.remove(19)
my_list.remove(56)
my_list.travel()
print("{:#^50}".format(""))
print("search操作:")
print(my_list.search(1000))
print(my_list.search(101))
print("链表长为:{0}".format(my_list.length()))
4、链表与顺序表的区别
操作 | 链表时间 | 顺序表时间 |
---|---|---|
表头插入元素add | O(1) | O(n) |
表尾插入元素append | O(n) | O(1) |
在任意位置插入元素insert | O(n) | O(n) |
访问元素 | O(n) | O(1) |
5、关于current和current.next的区别
A4 和 A5
current指的是当前整个节点,而current.next指的是下一个节点的地址
图片由python Tutor生成
用两个函数来测试分别用current和current.next做while循环终止条件,count遍历了多少次,我猜测是用current 比 current.next 的count要多一次。
def traverse_next(self):
current = self._head
count = 0;
if self.is_empty():
return count
else:
while current.next is not None:
count += 1
current = current.next
return count
def traverse(self):
current = self._head
count = 0
if self.is_empty():
return count
else:
while current is not None:
count += 1
current = current.next
return count
和我的猜测一样,current—>7 , current.next —> 6
将上述代码修改为如下,加上一个返回current.data。会发生什么?
def traverse_next(self):
current = self._head
count = 0;
if self.is_empty():
return count
else:
while current.next is not None:
count += 1
current = current.next
return count,current.data
def traverse(self):
current = self._head
count = 0
if self.is_empty():
return count
else:
while current is not None:
count += 1
current = current.next
return count, current.data
代码结果表明:虽然current和current.next 都能当遍历的条件,但是current.next可以返回尾结点的数值域,而current将一整个节点遍历完,最后current = None了,当调用current.data自然会报错。
用append()在表尾增加元素和遍历单链表说明什么时候用current和什么时候用current.next
'''在链表尾部插入元素,尾插法'''
def append(self, item):
node = Node(item)
if self.is_empty():
self._head = node
else:
current = self._head
while current.next is not None:
current = current.next
current.next = node
def append2(self,item):
node = Node(item)
if self.is_empty():
self._head = node
else:
current = self._head
previous = None
while current is not None:
previous = current
current = current.next
previous.next = node
'''遍历单链表'''
def travel(self):
current = self._head
while current is not None:
print("{0}".format(current.data), end=" ")
current = current.next
print(" ")
def travel2(self):
current = self._head
while current.next is not None:
print("{0}".format(current.data), end=" ")
current = current.next
print(" ")
if __name__ == "__main__":
my_list = SingleList()
my_list.append(100)
my_list.append(101)
my_list.append(102)
my_list.append(103)
my_list.append2(104)
print("用current当循环的条件:")
my_list.travel()
print("{:#^50}".format(""))
print("用current.next当循环的条件:")
my_list.travel2()
print(my_list.length())
在向表尾添加元素的时候用current.next方便,遍历链表用current,用current.next会少表尾最后一个元素。
A2
由面向过程的C语言和面向对象的python去实现一个链表的感受:
- 代码变少了,简洁的背后是需要对面向对像的熟悉,python万物皆对象,每个对象都有三要素 id type value 变量存的是地址。比如C中第一个节点是 current = L->next,python中是 current = self._head
- C语言一步一步实现也不能忘记,更容易去思考底层的原理,反而面向对象高级语言,操作简单,但感觉让人变笨了的感觉。