数据结构

概念:数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成

算法复杂度

时间复杂度

时间复杂度是同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。
计算机科学中,算法的时间复杂度是一个函数,它定性描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况

计算方法

1.一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
分析:随着模块n的增大,算法执行的时间的增长率和 f(n) 的增长率成正比,所以 f(n) 越小,算法的时间复杂度越低,算法的效率越高。
2. 在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出 T(n) 的同数量级(它的同数量级有以下:1,log2n,n,n log2n ,n的平方,n的三次方,2的n次方,n!),找出后,f(n) = 该数量级,若 T(n)/f(n) 求极限可得到一常数c,则时间复杂度T(n) = O(f(n))

空间复杂度

空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。比如直接插入排序时间复杂度是O(n^2),空间复杂度是O(1) 。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息。一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量

对于一个算法,其 时间复杂度和空间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。另外,算法的所有性能之间都存在着或多或少的相互影响。因此,当设计一个算法(特别是大型算法)时,要综合考虑算法的各项性能,算法的使用频率,算法处理的数据量的大小,算法描述语言的特性,算法运行的机器系统环境等各方面因素,才能够设计出比较好的算法。算法的时间复杂度和空间复杂度合称为算法的复杂度

线性表

顺序表

顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中

链表

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间。

而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)

相关术语:

前驱指针:指向上一个节点

后继指针:指向下一节点

单向链表: 一个节点,只包含一个指针,就是后继指针,用来指向下一节点。尾节点指向None

双向链表:一个节点,包含两个指针,前驱、后继指针。注意点 修改一个节点的时候,要记住前后节点的位置

单向循环:一个节点,只包含一个指针,用来指向下一节点,尾节点的下一节点就是头节点

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

单向链表

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

双向链表

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

单向循环链表

排序

排序算法的稳定&不稳定性

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

冒泡排序

概念:冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法

它重复地走访过要排序的元素列,一次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
冒泡排序算法的原理如下:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  1. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  2. 针对所有的元素重复以上的步骤,除了最后一个。
  3. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
  4. 代码理解:就是借用两个for 循环(内循环 外循环)
  1. # coding:utf-8
  2. def bubble_sort(alist):
  3. """冒泡排序"""
  4. n = len(alist)
  5. for j in range(n-1):
  6. for i in range(0, n-1-j):
  7. # 从头走到尾
  8. if alist[i] > alist[i+1]:
  9. alist[i],alist[i+1] = alist[i+1], alist[i]
  10. if __name__ == "__main__":
  11. li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
  12. print(li)
  13. bubble_sort(li)
  14. print(li)

冒泡排序

选择排序

选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

  注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。

  1. def select_sort(alist):
  2. """选择排序"""
  3. n = len(alist)
  4. for j in range(n-1): # j: 0 ~ n-2
  5. min_index = j
  6. for i in range(j+1, n):
  7. if alist[min_index] > alist[i]:
  8. min_index = i
  9. alist[j], alist[min_index] = alist[min_index], alist[j]
  10.  
  11. if __name__ == "__main__":
  12. li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
  13. print(li)
  14. select_sort(li)
  15. print(li)

选择排序

插入排序

插入排序算法有种递归的思想在里面,它由N-1趟排序组成。初始时,只考虑序列下标0处的元素,只有一个元素,显然是有序的。

然后第一趟 对下标 1 处的元素进行排序,保证序列[0,1]上的元素有序;

第二趟 对下标 2 处的元素进行排序,保证序列[0,2]上的元素有序;

.....

.....

第N-1趟对下标 N-1 处的元素进行排序,保证序列[0,N-1]上的元素有序,也就是整个数组有序了。

它的递归思想就体现在:当对位置 i 处的元素进行排序时,[0,i-1]上的元素一定是已经有序的了。

  1. # coding:utf-8
  2.  
  3. def insert_sort(alist):
  4. """插入排序"""
  5. n = len(alist)
  6. # 从右边的无序序列中取出多少个元素执行这样的过程
  7. for j in range(1, n):
  8. # j = [1, 2, 3, n-1]
  9. # i 代表内层循环起始值
  10. i = j
  11. # 执行从右边的无序序列中取出第一个元素,即i位置的元素,然后将其插入到前面的正确位置中
  12. while i > 0:
  13. if alist[i] < alist[i-1]:
  14. alist[i], alist[i-1] = alist[i-1], alist[i]
  15. i -= 1
  16. else:
  17. break
  18.  
  19. if __name__ == "__main__":
  20. li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
  21. print(li)
  22. insert_sort(li)
  23. print(li)

插入排序

希尔排序

思想:将待排序列划分为若干组,在每一组内进行插入排序,以使整个序列基本有序,然后再对整个序列进行插入排序。

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量  =1(  <  …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
该方法实质上是一种分组插入方法
比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。

  1. # coding:utf-8
  2.  
  3. def shell_sort(alist):
  4. """希尔排序"""
  5. # n=9
  6. n = len(alist)
  7. # gap =4
  8. gap = n // 2 # // 整除取整数
  9. # gap变化到0之前,插入算法执行的次数
  10. while gap > 0:
  11. # 插入算法,与普通的插入算法的区别就是gap步长
  12. for j in range(gap, n):
  13. # j = [gap, gap+1, gap+2, gap+3, ..., n-1]
  14. i = j
  15. while i > 0:
  16. if alist[i] < alist[i-gap]:
  17. alist[i], alist[i-gap] = alist[i-gap], alist[i]
  18. i -= gap
  19. else:
  20. break
  21. print('while:',alist)
  22. # 缩短gap步长
  23. print('for:',alist)
  24. gap //= 2
  25.  
  26. if __name__ == "__main__":
  27. li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
  28. print(li)
  29. shell_sort(li)
  30. print(li)
  31.  
  32. """
  33. [54, 26, 93, 17, 77, 31, 44, 55, 20]
  34. while: [54, 26, 93, 17, 77, 31, 44, 55, 20]
  35. while: [54, 26, 93, 17, 77, 31, 44, 55, 20]
  36. while: [54, 26, 55, 17, 77, 31, 93, 44, 20]
  37. while: [54, 26, 55, 17, 77, 31, 93, 44, 20]
  38. while: [20, 26, 55, 17, 54, 31, 93, 44, 77]
  39. for: [20, 26, 55, 17, 54, 31, 93, 44, 77]
  40. while: [20, 26, 55, 17, 54, 31, 93, 44, 77]
  41. while: [20, 77, 55, 26, 54, 31, 93, 44, 17]
  42. while: [20, 77, 54, 26, 55, 31, 93, 44, 17]
  43. while: [20, 77, 54, 26, 55, 31, 93, 44, 17]
  44. while: [20, 77, 54, 26, 55, 31, 93, 44, 17]
  45. while: [20, 77, 54, 26, 55, 31, 93, 44, 17]
  46. while: [17, 77, 20, 26, 54, 31, 55, 44, 93]
  47. for: [17, 77, 20, 26, 54, 31, 55, 44, 93]
  48. while: [17, 77, 20, 26, 54, 31, 55, 44, 93]
  49. while: [17, 20, 77, 26, 54, 31, 55, 44, 93]
  50. while: [17, 20, 26, 77, 54, 31, 55, 44, 93]
  51. while: [17, 20, 26, 54, 77, 31, 55, 44, 93]
  52. while: [17, 20, 26, 31, 54, 77, 55, 44, 93]
  53. while: [17, 20, 26, 31, 54, 55, 77, 44, 93]
  54. while: [17, 20, 26, 31, 44, 54, 55, 77, 93]
  55. while: [17, 20, 26, 31, 44, 54, 55, 77, 93]
  56. for: [17, 20, 26, 31, 44, 54, 55, 77, 93]
  57. [17, 20, 26, 31, 44, 54, 55, 77, 93]
  58. """

希尔排序

快速排序

设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
  1. # coding:utf-8
  2. def quick_sort(alist, first, last):
  3. """快速排序"""
  4. if first >= last:
  5. return
  6. mid_value = alist[first]
  7. low = first
  8. high = last
  9. while low < high:
  10. # high 左移
  11. while low < high and alist[high] >= mid_value:
  12. high -= 1
  13. alist[low] = alist[high]
  14.  
  15. while low < high and alist[low] < mid_value:
  16. low += 1
  17. alist[high] = alist[low]
  18. # 循环退出时,满足条件low==high
  19. alist[low] = mid_value
  20.  
  21. # 对low左边的列表执行快速排序
  22. quick_sort(alist, first, low-1)
  23.  
  24. # 对low右边的列表排序
  25. quick_sort(alist, low+1, last)
  26.  
  27. if __name__ == "__main__":
  28. li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
  29. print(li)
  30. quick_sort(li, 0, len(li)-1)
  31. print(li)

快速排序

归并排序

概念:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11;
逆序数为14;
  1. # coding:utf-8
  2.  
  3. def merge_sort(alist):
  4. """归并排序"""
  5. n = len(alist)
  6. if n <= 1:
  7. return alist
  8. mid = n // 2
  9.  
  10. # left 采用归并排序后形成的有序的新的列表
  11. left_li = merge_sort(alist[:mid])
  12.  
  13. # right 采用归并排序后形成的有序的新的列表
  14. right_li = merge_sort(alist[mid:])
  15.  
  16. # 将两个有序的子序列合并为一个新的整体
  17. # merge(left, right)
  18. left_pointer, right_pointer = 0, 0
  19. result = []
  20.  
  21. while left_pointer < len(left_li) and right_pointer < len(right_li):
  22. if left_li[left_pointer] <= right_li[right_pointer]:
  23. result.append(left_li[left_pointer])
  24. left_pointer += 1
  25. else:
  26. result.append(right_li[right_pointer])
  27. right_pointer += 1
  28.  
  29. result += left_li[left_pointer:]
  30. result += right_li[right_pointer:]
  31. return result
  32.  
  33. if __name__ == "__main__":
  34. li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
  35. print(li)
  36. sorted_li = merge_sort(li)
  37. print(li)
  38. print(sorted_li)
  39.  
  40. """
  41. [54, 26, 93, 17, 77, 31, 44, 55, 20]
  42. [54, 26, 93, 17, 77, 31, 44, 55, 20]
  43. [17, 20, 26, 31, 44, 54, 55, 77, 93]
  44. """

归并排序

搜索

二分查找

说明:元素必须是有序的,如果是无序的则要先进行排序操作。

  基本思想:也称为是折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点。

  复杂度分析:最坏情况下,关键词比较次数为log2(n+1),且期望时间复杂度为O(log2n);

  1. def binary_search(alist, item):
  2. """二分查找,递归"""
  3. n = len(alist)
  4. if n > 0:
  5. mid = n//2
  6. if alist[mid] == item:
  7. return True
  8. elif item < alist[mid]:
  9. return binary_search(alist[:mid], item)
  10. else:
  11. return binary_search(alist[mid+1:], item)
  12. return False
  13.  
  14. def binary_search_2(alist, item):
  15. """二分查找, 非递归"""
  16. n = len(alist)
  17. first = 0
  18. last = n-1
  19. while first <= last:
  20. mid = (first + last)//2
  21. if alist[mid] == item:
  22. return True
  23. elif item < alist[mid]:
  24. last = mid - 1
  25. else:
  26. first = mid + 1
  27. return False
  28.  
  29. if __name__ == "__main__":
  30. li = [17, 20, 26, 31, 44, 54, 55, 77, 93]
  31. print(binary_search(li, 55))
  32. print(binary_search(li, 100))
  33. print(binary_search_2(li, 55))
  34. print(binary_search_2(li, 100))
  35. """
  36. True
  37. False
  38. True
  39. False
  40. """

二分查找法

二叉树

在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2^{i-1}个结点;深度为k的二叉树至多有2^k-1个结点;对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_2+1。
一棵深度为k,且有2^k-1个节点的二叉树,称为满二叉树。这种树的特点是每一层上的节点数都是最大节点数。而在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树。具有n个节点的完全二叉树的深度为log2(n+1)。深度为k的完全二叉树,至少有2^(k-1)个节点,至多有2^k-1个节点

相关术语:

树的结点(node):包含一个数据元素及若干指向子树的分支;
孩子结点(child node):结点的子树的根称为该结点的孩子;
双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;
兄弟结点:同一双亲的孩子结点; 堂兄结点:同一层上结点;
祖先结点: 从根到该结点的所经分支上的所有结点子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙
结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推;
树的深度:树中最大的结点层
结点的度:结点子树的个数
树的度: 树中最大的结点度。
叶子结点:也叫终端结点,是度为 0 的结点;
分枝结点:度不为0的结点;
有序树:子树有序的树,如:家族树;
无序树:不考虑子树的顺序; [3]

二叉树性质

(1) 在非空二叉树中,第i层的结点总数不超过  , i>=1;
(2) 深度为h的二叉树最多有  个结点(h>=1),最少有h个结点;
(3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
(4) 具有n个结点的完全二叉树的深度为  (注:[ ]表示向下取整)
(5)有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:若I为结点编号则 如果I>1,则其父结点的编号为I/2;
如果2*I<=N,则其左儿子(即左子树的根结点)的编号为2*I;若2*I>N,则无左儿子;如果2*I+1<=N,则其右儿子的结点编号为2*I+1;若2*I+1>N,则无右儿子。
(6)给定N个节点,能构成h(N)种不同的二叉树。h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。
(7)设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i [4]

完全二叉树

若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

满二叉树

除最后一层无任何子节点外,每一层上的所有结点都有两个子结点二叉树。
定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
 

  1. # coding:utf-8
  2.  
  3. class Node(object):
  4. """"""
  5. def __init__(self, item):
  6. self.elem = item
  7. self.lchild = None
  8. self.rchild = None
  9.  
  10. class Tree(object):
  11. """二叉树"""
  12. def __init__(self):
  13. self.root = None
  14.  
  15. def add(self, item):
  16. node = Node(item)
  17. if self.root is None:
  18. self.root = node
  19. return
  20. queue = [self.root]
  21. while queue:
  22. cur_node = queue.pop(0)
  23. if cur_node.lchild is None:
  24. cur_node.lchild = node
  25. return
  26. else:
  27. queue.append(cur_node.lchild)
  28. if cur_node.rchild is None:
  29. cur_node.rchild = node
  30. return
  31. else:
  32. queue.append(cur_node.rchild)
  33.  
  34. def breadth_travel(self):
  35. """广度遍历"""
  36. if self.root is None:
  37. return
  38. queue = [self.root]
  39. while queue:
  40. cur_node = queue.pop(0)
  41. print(cur_node.elem, end=" ")
  42. if cur_node.lchild is not None:
  43. queue.append(cur_node.lchild)
  44. if cur_node.rchild is not None:
  45. queue.append(cur_node.rchild)
  46.  
  47. def preorder(self, node):
  48. """先序遍历"""
  49. if node is None:
  50. return
  51. print(node.elem, end=" ")
  52. self.preorder(node.lchild)
  53. self.preorder(node.rchild)
  54.  
  55. def inorder(self, node):
  56. """中序遍历"""
  57. if node is None:
  58. return
  59. self.inorder(node.lchild)
  60. print(node.elem, end=" ")
  61. self.inorder(node.rchild)
  62.  
  63. def postorder(self, node):
  64. """后序遍历"""
  65. if node is None:
  66. return
  67. self.postorder(node.lchild)
  68. self.postorder(node.rchild)
  69. print(node.elem, end=" ")
  70.  
  71. if __name__ == "__main__":
  72. tree = Tree()
  73. tree.add(0)
  74. tree.add(1)
  75. tree.add(2)
  76. tree.add(3)
  77. tree.add(4)
  78. tree.add(5)
  79. tree.add(6)
  80. tree.add(7)
  81. tree.add(8)
  82. tree.add(9)
  83. tree.breadth_travel()
  84. print(" ")
  85. tree.preorder(tree.root)
  86. print(" ")
  87. tree.inorder(tree.root)
  88. print(" ")
  89. tree.postorder(tree.root)
  90. print(" ")
  91.  
  92. """
  93. 0 1 2 3 4 5 6 7 8 9
  94. 0 1 3 7 8 4 9 2 5 6
  95. 7 3 8 1 9 4 0 5 2 6
  96. 7 8 3 9 4 1 5 6 2 0
  97. """

二叉树

先序遍历: 根 左 右

中序遍历: 左 根 右

后序遍历: 左 右 根

 三种顺序之中:如果知道中序跟其中一个 ,就可以写出第三个顺序来

初始数据结构(python语言)的更多相关文章

  1. 0.数据结构(python语言) 基本概念 算法的代价及度量!!!

    先看思维导图: *思维导图有点简陋,本着循循渐进的思想,这小节的知识大多只做了解即可. *重点在于算法的代价及度量!!!查找资料务必弄清楚. 零.四个基本概念 问题:一个具体的需求 问题实例:针对问题 ...

  2. 数据结构(python语言)目录链接

    第一章 准备工作 课时0:0.数据结构(python语言) 基本概念 算法的代价及度量!!!

  3. 用python语言讲解数据结构与算法

    写在前面的话:关于数据结构与算法讲解的书籍很多,但是用python语言去实现的不是很多,最近有幸看到一本这样的书籍,由Brad Miller and David Ranum编写的<Problem ...

  4. Python语言数据结构和语言结构(2)

    目录 1. Python预备基础 2. Python数据类型 3. Python条件语句 4. while循环和for循环 1. Python预备基础 1.1 变量的命名   变量命名规则主要有以下几 ...

  5. 五种编程语言解释数据结构与算法——顺序表3(JavaScript与Python语言实现)

    7.JavaScript语言实现 7.1.用ES6语法编写顺序表类 //1.创建类 class MyList { //1. initList(&L):初始化表.构造一个空的线性表.放回值应该是 ...

  6. Python语言学习之C++调用python

    C++调用python 在C/C++中嵌入Python,可以使用Python提供的强大功能,通过嵌入Python可以替代动态链接库形式的接口,这样可以方便地根据需要修改脚本代码,而不用重新编译链接二进 ...

  7. 使用Python语言理解递归

    递归 一个函数在执行过程中一次或多次调用其本身便是递归,就像是俄罗斯套娃一样,一个娃娃里包含另一个娃娃. 递归其实是程序设计语言学习过程中很快就会接触到的东西,但有关递归的理解可能还会有一些遗漏,下面 ...

  8. 强者联盟——Python语言结合Spark框架

    引言:Spark由AMPLab实验室开发,其本质是基于内存的高速迭代框架,"迭代"是机器学习最大的特点,因此很适合做机器学习. 得益于在数据科学中强大的表现,Python语言的粉丝 ...

  9. Python语言程序设计之三--列表List常见操作和错误总结

    最近在学习列表,在这里卡住了很久,主要是课后习题太多,而且难度也不小.像我看的这本<Python语言程序设计>--梁勇著,列表和多维列表两章课后习题就有93道之多.我的天!但是题目出的非常 ...

随机推荐

  1. 从锅炉工到AI专家(3)

    剖析第一个例子 学习<机器学习>,很多IT高手是直接去翻看TensorFlow文档,但碰壁的很多.究其原因,TensorFlow的文档跨度太大了,它首先假设你已经对"机器学习&q ...

  2. 一个JavaWeb搭建的开源Blog系统,整合SSM框架

    搬砖有暇,捣鼓了一个简单的Blog系统(项目地址https://github.com/lenve/JavaEETest/tree/master/MyBlog),适合以下人群学习: 1.已经掌握了jsp ...

  3. 记一次Eureka启动报Failed to start bean 'eurekaAutoServiceRegistration' 。。。错误

    在一次项目迁移的过程中,新导入了两个依赖,结果项目启动就报错,如下: 主要原因是:Failed to start bean 'eurekaAutoServiceRegistration'; neste ...

  4. putty 默认颜色样式修改 for windows

    一.导出 putty 注册表默认配置文件 1.1 打开注册表:运行 --» regedit 找到 putty 注册表文件: [HKEY_CURRENT_USER\Software\SimonTatha ...

  5. Linux基础知识第一讲,基本目录结构与基本命令

    目录 一丶Window 与 Linux的目录结构 1.Windows 与 Linux目录简介 2.Linux目录主要作用 3.任务栏与菜单栏,与关闭按钮 二丶Linux终端与常见命令学习 1.终端中的 ...

  6. Oracle 连接 另一个Oracle数据库 服务器连接

    一.场景   两台不同的服务器A.B分别装有不同业务的oracle数据库,因业务需要,现需要将B中test表的数据,定时同步到A中. 二.实现   根据以上场景,我想到了oracle中的dblink, ...

  7. C# 操作Excel图形——绘制、读取、隐藏、删除图形

    简介 本篇文章将介绍C# 如何处理Excel图形相关的问题,包括以下内容要点: 1.绘制图形 1.1 绘制图形并添加文本到图形 1.2 添加图片到图形 1.3 设置图形阴影效果 1.4 设置图形透明度 ...

  8. SpringMVC页面向Controller传参

    关于SpringMVC页面向Controller传参的问题,看了网上不少帖子,大多总结为以下几类: 1.直接把页面表单中相关元素的name属性对应的值作为Controller方法中的形参. 这个应该是 ...

  9. 原生JS实现简易轮播图

    原生JS实现简易轮播图(渐变?) 最近做网页总是会用到轮播图,我就把之前写的轮播图单独拿出来吧,如果有...如果真的有人也需要也可以复制去用用啊..哈~.. window.onload = funct ...

  10. Adaptive Placeholders

    https://wisdmlabs.com/blog/create-adaptive-placeholders-using-css/ https://circleci.com/blog/adaptiv ...