算法和数据结构的关系
如果要讨论算法,就无法避免的讨论数据结构。
为什么?
这得从算法和数据结构的关系讲起了
举个例子,蛋炒饭。要做蛋炒饭,我们需要热锅、倒油、打蛋等,这些就是算法,而米饭、鸡蛋、油、盐就是数据结构。
我们除了掌握各种算法之外,还需要掌握数据结构。
否则,就像这张图。
那么,到底什么是数据结构呢?
数据结构(Data Structure)是数据的组织、管理和存储格式,其使用目的是为了高效地访问和修改数据。
数据结构通常可分为4类。
线性结构
线性结构是最简单的数据结构,包括数组、链表,以及由它们衍生出来的栈、队列、哈希表。
树
树是相对复杂的数据结构,其中比较有代表性是二叉树,由它又衍生出了二叉堆之类的数据结构。
图
图是更为复杂的数据结构,因为在图中会呈现出多对多的关联关系。
其他数据结构
除上述所列的几种基本数据结构以外,还有一些其他的千奇百怪的数据结构。它们由基本数据结构变形而来,用于解决某些特定问题,如跳表、哈希链表、位图等。
数据结构和算法是相辅相成的。
数据结构是为算法服务的,算法要作用在特定的数据结构之上。
现在,我们开始讨论数据结构。
数组的定义
我们讨论的第一个数据结构是数组 。
为什么数组从0开始编号?
要回答这个问题,我们先来讨论一下什么是数组。
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
这里有两个核心点。
线性表
连续的内存空间和相同类型的数据
线性表
首先,什么是线性表?
线性表(Linear List),顾名思义,就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。常见的线性表,除了数组外,还有之后会讨论的链表、队列、栈等。
与线性表相对立的概念是非线性表,比如二叉树、堆、图等。在非线性表中,其特点是数据之间并不是简单的前后关系。
连续的内存空间和相同类型的数据
这是一个"一箭双雕"的特点,既有弊,又有利。
弊 :插入和删除非常的低效
利 :读取非常的高效
对于插入和删除
比如,现在有a
、b
、c
、d
、e
,一共5个元素。现在我们要在2这个位置插入一个元素x
。
我们需要怎么做?
首先,我们需要把把c
、d
和e
都依次往后移动,把第二个位置腾出来,然后把x
放进去。也就是说,数组中插入一个元素的话,可能会导致数组中所有的元素都要移动位置。删除也是同样的道理。
那么,如果数组的长度就是5,我们还要再插入一个元素呢?
理论上这个是没法做的,但如果一定要这么做,也不是没有办法。
数组容器 ,我们很快就会讨论这个。
对于读取
对于读取,那这个就很好办了。
你不是连续的内存空间吗?你不是相同类型的数据吗?
相同类型的数据就意味着每一个元素做占用的内存空间是一致的。
比如,int[] a = new int[10]
,第一个元素的内存空间是100到103,那么我们要找第三个元素,怎么找?108到111,就是第三个元素。
数组的寻址
在讨论线性表
、连续的内存空间和相同类型的数据
这两个特点后,我们终于可以回答为什么数组从0开始编号了。
因为"线性表",所以数组中的元素只有前后关系;又因为"连续的内存空间和相同类型的数据";我们可以得到数组寻址公式如下:
a[i]Address = baseAddress + i ∗ dataTypeSize \text{a[i]Address} = \text{baseAddress} + \text{i} * \text{dataTypeSize}
a[i]Address = baseAddress + i ∗ dataTypeSize
有没有发现什么?
我们要找数组中的第一个元素,i的值是多少?0。第二个元素呢?i的值是1。
所以,所谓的"下标",可以理解成"偏移(offset)"。a[0]
就是偏移为0的位置,也就是首地址;a[k]
就表示偏移k个type_size的位置。
这就是为什么数组从0开始编号。
数组容器
数组和数组容器
现在,我们来做一件事情。我们定义一个数组,赋值,打印。
示例代码:
1 2 3 4 5 6 7 8 9 10 ArrayList<Integer> a = new ArrayList<Integer>(5 ); a.add(1 ); a.add(2 ); a.add(3 ); a.add(4 ); a.add(5 ); System.out.println(a); a.add(6 ); System.out.println(a);
运行结果:
1 2 [1, 2, 3, 4, 5] [1, 2, 3, 4, 5, 6]
示例代码:
1 2 3 4 a = [1 , 2 , 3 , 4 , 5 ] print(a) a.append(6 ) print(a)
运行结果:
1 2 [1, 2, 3, 4, 5] [1, 2, 3, 4, 5, 6]
这个好像不对啊?说好的长度是5,怎么还把第6个元素加进去了呢?
来,我们换一下。
示例代码:
1 2 3 4 5 6 7 8 9 int [] a = new int [5 ];a[0 ] = 1 ; a[1 ] = 2 ; a[2 ] = 3 ; a[3 ] = 4 ; a[4 ] = 5 ; System.out.println(Arrays.toString(a)); a[5 ] = 6 ; System.out.println(a);
运行结果:
1 2 3 4 [1, 2, 3, 4, 5] Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 at ch02.TestArray.func2(TestArray.java:34) at ch02.TestArray.main(TestArray.java:10)
示例代码:
1 2 3 4 5 6 7 8 9 a = [None ] * 5 a[0 ] = 1 a[1 ] = 2 a[2 ] = 3 a[3 ] = 4 a[4 ] = 5 print(a) a[5 ] = 6 print(a)
运行结果:
1 2 3 4 5 [1, 2, 3, 4, 5] Traceback (most recent call last): File "/Users/kaka/Documents/algorithm-data-structures-for-blog/Python/ch02/TestArray.py", line 13, in <module> a[5] = 6 IndexError: list assignment index out of range
看!报错了吧!报错就对了。
在Java中,我们用的ArrayList
,就不是数组,而是数组容器。
那,Python呢?在Python中,我们用的那玩意就不叫数组,是List。(也是容器的一种)
实现一个数组容器
现在,我们来实现一个数组容器。
那我们实现哪些方法?
除了那个remove(Object o)
,因为我们在我们这里index和element都是int类型,没法实现。
示例代码:
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 package ch02;public class MyArrayList { private int [] array; private int size; public MyArrayList (int capacity) { this .array = new int [capacity]; size = 0 ; } public void add (int index, int element) throws Exception { if (index<0 || index>size){ throw new IndexOutOfBoundsException("超出数组实际元素范围!" ); } if (size >= array.length){ resize(); } for (int i=size-1 ; i>=index; i--){ array[i+1 ] = array[i]; } array[index] = element; size++; } public void add (int element) { if (size >= array.length){ resize(); } array[size] = element; size++; } public void resize () { int [] arrayNew = new int [array.length*2 ]; System.arraycopy(array, 0 , arrayNew, 0 , array.length); array = arrayNew; } public int remove (int index) throws Exception { if (index<0 || index>=size){ throw new IndexOutOfBoundsException("超出数组实际元素范围!" ); } int deletedElement = array[index]; for (int i=index; i<(size-1 ); i++){ array[i] = array[i+1 ]; } size--; return deletedElement; } public void output () { String rnt = "" ; for (int i = 0 ; i < size; i++) { rnt = rnt + array[i] + " " ; } System.out.println(rnt); } public static void main (String[] args) throws Exception { MyArrayList myArrayList = new MyArrayList(3 ); myArrayList.add(0 ,1 ); myArrayList.add(1 ,2 ); myArrayList.add(2 ,3 ); myArrayList.output(); myArrayList.add(4 ); myArrayList.add(5 ); myArrayList.add(6 ); myArrayList.output(); myArrayList.remove(1 ); myArrayList.remove(1 ); myArrayList.remove(1 ); myArrayList.output(); } }
运行结果:
1 2 3 1 2 3 1 2 3 4 5 6 1 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 class MyArrayList : def __init__ (self, capacity) : """ :param capacity: 长度 """ self.array = [None ] * capacity self.size = 0 def add_index_element (self, index, element) : """ 在指定位置添加元素 :param index: 指定位置 :param element: 元素 :return: """ if index < 0 or index > self.size: raise Exception("超出数组实际元素范围!" ) if self.size >= len(self.array): self.resize() for i in range(self.size - 1 , index - 1 , -1 ): self.array[i + 1 ] = self.array[i] self.array[index] = element self.size += 1 def add_element (self, element) : """ 在尾部放入新元素 :param element: 放在尾部的元素 :return: """ if self.size >= len(self.array): self.resize() self.array[self.size] = element self.size += 1 def add (self, *args) : """ add :param args: :return: """ if len(args) == 2 : self.add_index_element(index=args[0 ], element=args[1 ]) if len(args) == 1 : self.add_element(element=args[0 ]) def resize (self) : """ 数组扩容 :return: """ array_new = [None ] * len(self.array) * 2 for i in range(self.size): array_new[i] = self.array[i] self.array = array_new def remove (self, index) : """ 移除指定位置的数组 :param index: 指定的位置 :return: """ if index < 0 or index >= self.size: raise Exception("超出数组实际元素范围!" ) for i in range(index, self.size-1 ): self.array[i] = self.array[i + 1 ] self.size -= 1 def output (self) : """ 输出数组 :return: """ print(self.array[:self.size]) myArrayList = MyArrayList(3 ) myArrayList.add(0 , 1 ) myArrayList.add(1 , 2 ) myArrayList.add(2 , 3 ) myArrayList.output() myArrayList.add(4 ) myArrayList.add(5 ) myArrayList.add(6 ) myArrayList.output() myArrayList.remove(1 ) myArrayList.remove(1 ) myArrayList.remove(1 ) myArrayList.output()
运行结果:
1 2 3 [1, 2, 3] [1, 2, 3, 4, 5, 6] [1, 5, 6]
链表的结构
正如我们之前的讨论,数组的优点是读取快,缺点是插入和删除慢。
现在有一个数据结构,它的优点就是数组的缺点,而它的缺点也就是数组的优点。
链表。
链表读取慢,但是插入和删除快。
除了这两点不同外,数组和链表还有一个不同的地方。数组要求是在内存中的连续空间,而链表却可以是内存中的离散空间。
链表有很多种,有单链表、循环链表、双向链表还有双向循环链表。
这些链表我们都会讨论。
从单链表开始。
单链表
如图,就是单链表的结构。每一个节点除了存储数据之外,还记录下一个节点的地址。通过这种方法,把离散的内存空间串联起来。
但有两个节点不太一样。
最后一个节点,它的next指向的是NULL。
第一个节点,没有任何指针指向它,这个节点也被称为头节点。有了头节点,我们可以遍历得到整个链表。
这个像什么?
带鱼咬尾巴,一钓一大串。
那么,为什么链表读取很慢,但是增加和删除很快?
关于读取慢,我想这个很好解释。我们要从头节点开始,通过一个又一个的指针,才能读取到我们想要的内容。比如,我们要读取链表的第N个数字,那么我们就需要N-1个指针,一个又一个的指过去,才能读取到第N个数字。而在数组中,通过数组寻址,一步到位。
那么,为什么增加和删除很快呢?
来看看增加和删除的方法。
比如,在b和c之间,我们想插入一个节点x,在数组中,我们需要先把c及c之后的节点都后移,有多少个就需要后移多少个;通过这个方法,把位置空出来,再把x放进去。但在链表中,把b的指针从指向c改成指向x,再把x的指针指向c,搞定!
对于删除,在数组中,如果我们要在把b节点删除,我们需要把c及c之后的节点都前移,有多少个就需要前移动多少个。而在链表中,把a节点从指向b改成指向c,再把b节点中指向c的指针断开。
为什么要把b的指针断开?如果不断开?
乱了,乱了,乱了。
这样的话,我们可以把b节点作为头节点,然后遍历得到之后的所有数据。
其实,还有一个问题,这是题外话。就算我们把两个指针断开了,b节点不是还在吗?b节点本身不处理吗?这就是所谓的高级语言的高级之处了,自动化的垃圾回收机制,被删除的节点会被自动回收。
其他链表结构
关于其他类型的链表,我想我们用图就可以表示清楚了。
循环链表
双向链表
双向循环链表
链表容器
熟悉Java的同学们都知道,在Java中,除了有ArrayList
,还有LinkedList
。顾名思义,LinkedList
是基于链表的List。
现在,我们来实现一个LinkedList
。
实现这些方法。
除了remove(Object o)
其中remove()
是remove第一个节点
示例代码:
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 191 192 package ch02;public class MyLinkedList { private static class Node { int data; Node next; Node(int data) { this .data = data; } } private Node head; private Node last; private int size; public MyLinkedList () { } public Node get (int index) throws Exception { if (index<0 || index>=size) { throw new IndexOutOfBoundsException("超出链表节点范围!" ); } Node temp = head; for (int i=0 ; i<index; i++){ temp = temp.next; } return temp; } public void add (int index, int element) throws Exception { if (index<0 || index>size) { throw new IndexOutOfBoundsException("超出链表节点范围!" ); } Node insertedNode = new Node(element); if (size == 0 ){ head = insertedNode; last = insertedNode; } else if (index == 0 ){ insertedNode.next = head; head = insertedNode; }else if (size == index){ last.next = insertedNode; last = insertedNode; }else { Node prevNode = get(index-1 ); insertedNode.next = prevNode.next; prevNode.next = insertedNode; } size++; } public void add (int element) throws Exception { Node insertedNode = new Node(element); last.next = insertedNode; last = insertedNode; size++; } public Node remove (int index) throws Exception { if (index<0 || index>=size) { throw new IndexOutOfBoundsException("超出链表节点范围!" ); } Node removedNode = null ; if (index == 0 ){ removedNode = head; head = head.next; if (size == 1 ){ last = null ; } }else if (index == size-1 ){ Node prevNode = get(index-1 ); removedNode = prevNode.next; prevNode.next = null ; last = prevNode; }else { Node prevNode = get(index-1 ); Node nextNode = prevNode.next.next; removedNode = prevNode.next; prevNode.next = nextNode; } size--; return removedNode; } public Node remove () throws Exception { if (size < 1 ) { throw new IndexOutOfBoundsException("超出链表节点范围!" ); } Node removedNode = head; head = head.next; if (size == 1 ){ last = null ; } size--; return removedNode; } public void output () { String rnt = "" ; Node temp = head; while (temp!=null ) { rnt = rnt + temp.data + " " ; temp = temp.next; } System.out.println(rnt); } public static void main (String[] args) throws Exception { MyLinkedList myLinkedList = new MyLinkedList(); myLinkedList.add(0 ,1 ); myLinkedList.add(1 ,2 ); myLinkedList.add(2 ,3 ); myLinkedList.output(); myLinkedList.remove(); myLinkedList.remove(); myLinkedList.output(); myLinkedList.add(4 ); myLinkedList.add(5 ); myLinkedList.add(6 ); myLinkedList.output(); myLinkedList.remove(1 ); myLinkedList.remove(1 ); myLinkedList.output(); System.out.println(myLinkedList.get(1 ).data); } }
运行结果:
1 2 3 4 5 1 2 3 3 3 4 5 6 3 6 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 162 class Node : def __init__ (self, data) : """ :param data: """ self.data = data self.next = None class MyLinkedList : def __init__ (self) : self.size = 0 self.head = None self.last = None def get (self, index) : """ 获取指定位置的节点 :param index: :return: """ if index < 0 or index >= self.size: raise Exception("超出链表节点范围!" ) p = self.head for i in range(index): p = p.next return p def add_data (self, element) : """ 在尾部添加节点 :param element: :return: """ node = Node(element) self.last.next = node self.last = node self.size += 1 def add_index_element (self, element, index) : """ 在指定位置添加节点 :param element: :param index: :return: """ if index < 0 or index > self.size: raise Exception("超出链表节点范围!" ) node = Node(element) if self.size == 0 : self.head = node self.last = node elif index == 0 : node.next = self.head self.head = node elif self.size == index: self.last.next = node self.last = node else : prev_node = self.get(index - 1 ) node.next = prev_node.next prev_node.next = node self.size += 1 def add (self, *args) : """ 添加节点 :param args: :return: """ if len(args) == 2 : self.add_index_element(index=args[0 ], element=args[1 ]) if len(args) == 1 : self.add_data(element=args[0 ]) def remove_head (self) : """ 移除头部节点 :return: """ removed_node = self.head self.head = self.head.next if self.size == 1 : self.last == Node self.size -= 1 return removed_node def remove_index (self, index) : """ 移除指定位置的节点 :param index: :return: """ if index < 0 or index >= self.size: raise Exception("超出链表节点范围!" ) if index == 0 : removed_node = self.head self.head = self.head.next if self.size == 1 : self.last == Node elif index == self.size - 1 : prev_node = self.get(index - 1 ) removed_node = prev_node.next prev_node.next = None self.last = prev_node else : prev_node = self.get(index - 1 ) next_node = prev_node.next.next removed_node = prev_node.next prev_node.next = next_node self.size -= 1 return removed_node def remove (self, *args) : """ :param args: :return: """ if len(args) == 1 : self.remove_index(args[0 ]) if len(args) == 0 : self.remove_head() def output (self) : rnt = [] p = self.head while p is not None : rnt.append(p.data) p = p.next print(rnt) myLinkedList = MyLinkedList() myLinkedList.add(0 , 1 ) myLinkedList.add(1 , 2 ) myLinkedList.add(2 , 3 ) myLinkedList.output() myLinkedList.remove() myLinkedList.remove() myLinkedList.output() myLinkedList.add(4 ) myLinkedList.add(5 ) myLinkedList.add(6 ) myLinkedList.output() myLinkedList.remove(1 ) myLinkedList.remove(1 ) myLinkedList.output() print(myLinkedList.get(1 ).data)
运行结果:
1 2 3 4 5 [1, 2, 3] [3] [3, 4, 5, 6] [3, 6] 6
链表中的快慢指针
现在提一个问题,我们如何获取链表的中间节点。
这个很简单,size除以2,就是中间节点的index,然后就从头节点开始遍历,轻松搞定?
不是这样的。
size只在LinkedList
容器中有,如果这是链表,哪里有size?
那怎么办?
那我们先把整个链表都遍历一遍,获取链表的size,然后size除以2,再遍历一遍。这样的话,我们需要的遍历次数是
size + size 2 \text{size} + \frac{\text{size}}{2}
size + 2 size
现在,我们换一个思路。我们先用一个两倍速度的快指针遍历一遍,比如需要遍历N次,然后在用慢指针遍历N次。这样慢指针是不是恰好在中间的位置。这样的话,我们需要的遍历次数是
size 2 + size 2 \frac{\text{size}}{2} + \frac{\text{size}}{2}
2 size + 2 size
大致思路就是这样的。有几个细节还需要捋一捋。
这里解释一下:
只要快指针还能往下走,就继续往下走。
所以在图中偶数次的情况,fastTimes = 2
所以在图中奇数次的情况,fastTImes = 3
当快指针遍历结束时,其next节点不为空,说明有链表的size是偶数个,这时候中间节点有两个
当快指针遍历结束时,其next节点为空,说明链表的size是奇数个,这时候中间节点只有一个
示例代码:
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 package ch02;public class LinkedListMid { private static class Node { int data; Node next; Node(int data) { this .data = data; } } public static String getLinkedListMid (Node head) { String rnt = "" ; if (head == null ){ return null ; } Node fastNode = head; int fastTimes = 0 ; while (null != fastNode && null != fastNode.next && null != fastNode.next.next){ fastNode = fastNode.next.next; fastTimes = fastTimes + 1 ; } Node midNode = head; for (int i = 0 ; i < fastTimes; i++) { midNode = midNode.next; }; rnt = rnt + midNode.data; if (fastNode.next != null ){ rnt = rnt + " " + midNode.next.data; } return rnt; } public static void main (String[] args) { String midNode = "" ; Node test = null ; System.out.print("当链表为空,中间节点是 " ); System.out.print(getLinkedListMid(test)); System.out.print("\n" ); test = new Node(1 ); midNode = getLinkedListMid(test); System.out.print("当链表长度为1,中间节点是 " ); System.out.print(midNode); System.out.print("\n" ); test = new Node(1 ); test.next = new Node(2 ); midNode = getLinkedListMid(test); System.out.print("当链表长度为2,中间节点是 " ); System.out.print(midNode); System.out.print("\n" ); test = new Node(1 ); test.next = new Node(2 ); test.next.next = new Node(3 ); test.next.next.next = new Node(4 ); test.next.next.next.next = new Node(5 ); test.next.next.next.next.next = new Node(6 ); midNode = getLinkedListMid(test); System.out.print("当链表长度为6,中间节点是 " ); System.out.print(midNode); System.out.print("\n" ); test = new Node(1 ); test.next = new Node(2 ); test.next.next = new Node(3 ); test.next.next.next = new Node(4 ); test.next.next.next.next = new Node(5 ); test.next.next.next.next.next = new Node(6 ); test.next.next.next.next.next.next = new Node(7 ); midNode = getLinkedListMid(test); System.out.print("当链表长度为7,中间节点是 " ); System.out.print(midNode); System.out.print("\n" ); } }
运行结果:
1 2 3 4 5 当链表为空,中间节点是 null 当链表长度为1,中间节点是 1 当链表长度为2,中间节点是 1 2 当链表长度为6,中间节点是 3 4 当链表长度为7,中间节点是 4
示例代码:
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 class Node : def __init__ (self, data) : """ :param data: """ self.data = data self.next = None def getLinkedListMid (head) : """ 获取中间节点 :param head: 链表的head :return: 中间节点 """ rnt = [] if None == head: return None fastNode = head fastTimes = 0 while (fastNode is not None ) and (fastNode.next is not None ) and (fastNode.next.next is not None ): fastNode = fastNode.next.next fastTimes = fastTimes + 1 midNode = head for i in range(fastTimes): midNode = midNode.next rnt.append(midNode) if fastNode.next is not None : rnt.append(midNode.next) return rnt if __name__ == '__main__' : test = None print('当链表为空,中间节点是 ' , getLinkedListMid(test)) test = Node(1 ) midNodeList = getLinkedListMid(test) print('当链表长度为1,中间节点个数是 ' , len(midNodeList), ' 中间节点是 ' , midNodeList[0 ].data) test = Node(1 ) test.next = Node(2 ) midNodeList = getLinkedListMid(test) print('当链表长度为2,中间节点个数是 ' , len(midNodeList), ' 中间节点是 ' , midNodeList[0 ].data, ',' , midNodeList[1 ].data) test = Node(1 ) test.next = Node(2 ) test.next.next = Node(3 ) test.next.next.next = Node(4 ) test.next.next.next.next = Node(5 ) test.next.next.next.next.next = Node(6 ) midNodeList = getLinkedListMid(test) print('当链表长度为6,中间节点个数是 ' , len(midNodeList), ' 中间节点是 ' , midNodeList[0 ].data, ',' , midNodeList[1 ].data) test = Node(1 ) test.next = Node(2 ) test.next.next = Node(3 ) test.next.next.next = Node(4 ) test.next.next.next.next = Node(5 ) test.next.next.next.next.next = Node(6 ) test.next.next.next.next.next.next = Node(7 ) midNodeList = getLinkedListMid(test) print('当链表长度为7,中间节点个数是 ' , len(midNodeList), ' 中间节点是 ' , midNodeList[0 ].data)
运行结果:
1 2 3 4 5 当链表为空,中间节点是 null 当链表长度为1,中间节点是 1 当链表长度为2,中间节点是 1 2 当链表长度为6,中间节点个数是 3 4 当链表长度为7,中间节点个数是 4
关于链表,还有一类问题是链表反转。这个我们放在讨论递归的时候再进行讨论。因为链表反转本身的确没啥用,但却是一个很好的递归算法实践。