数据结构和算法笔记
目录
1.汉诺塔问题
汉诺塔问题可以分为三步(假设有n个盘子):
1.将n-1个盘子从a经过从移动到b
2.将第n个盘子从a移动到c
3.将n-1个盘子从b经过a移动到c
参数意义:
1.n:盘子的总个数
2.a:从哪个盘子 b:经过哪个盘子 c:到哪个盘子
def hanoi(n,a,b,c): if n>0: hanoi(n-1,a,c,b) print("moving from %s to %s" % (a,c)) hanoi(n-1,b,a,c)
2.顺序查找
参数意义:
1.li:列表
2.val:要查找的值
def linear_search(li,val): for ind,v in enumerate(li): if v == val: return ind else: return None
3.二分查找
二分查找需要注意以下几点:
1.二分查找存在候选区,当且仅当left<=right时,才会有候选区
2.根据left和right计算中间值mid的位置
3.用中间值和要查找的值做比较
1.如果要查找的值小于中间值mid,代表查找的值在左半部分,所以要将right位置移动到mid前面的位置
2.如果要查找的值大于中间值mid,代表查找的值在右半部分,所以要将left位置移动到mid后面的位置
3.如果要查找的值等于中间值mid,代表已经查找成功
参数意义:
1.li:列表
2.val:要查找的值
def binary_search(li,val): left = 0 right = len(li) - 1 while left <= right: mid=(left+right)//2 if li[mid] == val: return mid elif li[mid] > val: right = mid - 1 else: left = mid + 1 else: return None
4.冒泡排序
冒泡排序需要注意的点:
1.冒泡排序主要强调"趟"和"每趟需要交换多少次元素"
2.设立标志位,减少多余的趟
参数意义:
1.li:列表
def bubble_sort(li): for i in range(len(li)-1): # 第i趟 exchange = False for j in range(len(li)-i-1) # j代表箭头的位置 if li[j] > li[j+1]: li[j],li[j+1] = li[j+1],li[j] exchange = True if not exchange: return
5.选择排序
再一趟排序记录列表无序区最小的数,放到第二个位置
关键点:
有序区和无序区,无序区最小数的位置
选择排序需要注意的点:
在选择排序每趟的过程中,做了如下几件事:
1.先记录一下最小数的位置下标
2.从最小数的下一个数开始循环,让每一个数和最小数进行比较,如果循环中的数比最小数小,那么就将循环中的数的位置小标赋值给min_loc
直到这趟的循环全部结束,此时的min_loc值就是循环里的那些数中最小数的位置下标
3.如果现在得到的min_loc不是你在第1步记录的最小数的min_loc,说明最小数存在在循环中的那些数中,所以交换数值,将最小数交换到最前方(有序区中)
def select_sort(li): for i in range(len(li)-1): min_loc = i # 记录最小数的位置 for j in range(i+1,len(li)): if li[j] < li[min_loc]: # 如果现在这个数比最小数位置的数小 min_loc = j # 那么最小数的位置就是现在这个数的位置 if min_loc != i: # 只要最小数的位置不是原来的那个位置 li[i],li[min_loc] = li[min_loc],li[i]
6.插入排序
插入排序原理:
插入排序的原理类似于摸排,假如你手中有4.7.10三张牌,如果你摸到了一张牌为9,那么就把9插入到7和10中间的位置
关键点:
摸到的牌,手里的牌
插入排序需要注意的点:
1.每次循环开始,你都会摸到一张牌,暂时记录一下这张牌为tmp
2.j是你手里的牌的下标:如果我现在摸的牌下标为5,那么我手里牌的下标为4,3,2,1
现在让摸的牌[5]和手里的牌[4] 做比较
如果摸的牌[5]比手里的牌[4]小,那么将手里的牌右移一位,然后继续让摸的牌[5]和手里的牌[3]比较,直到摸的牌已经大于手里的牌,此时将摸到这张牌插入到j+1的位置
def insert_sort(li): for i in range(1,len(li)): # i代表你摸到的牌的下标 tmp = li[i] j = i - 1 # j指的是你手里的牌的下标 while j>=0 and tmp<li[j]: li[j+1]=li[j] # 将手里的牌右移一个位置 j=j-1 # 手里的牌下标向左移动一位 li[j+1] = tmp # 将摸到的牌放到j+1位置上
7.快速排序
1.快速排序思路
1.取一个元素p(一般是第一个元素)使元素p归位。
2.整个列表被p分成两部分,左边都比p小,右边都比p大。
3.递归完成排序。
2.快速排序框架代码
def quick_sort(data,left,right): if left < right: # 快速排序终止条件:列表中只有0个元素或者1个元素 mid = partition(data,left,right) # 调用归位函数,得到归位元素的下标 quick_sort(data,left,mid-1) # 对归位元素左半区列表做快速排序 quick_sort(data,mid+1,right) # 对归位元素右半区列表做快速排序
3.归位函数实现代码
def partition(li,left,right): tmp = li[left] # 将列表中的第一个元素作为归位元素 while left < right: while left < right and li[right] >= tmp: # 从右边找比tmp小的数 right-=1 # right指向向左移动一个位置 li[left] = li[right] # 把右边的值写到左边空位上 while left < right and li[left] <= tmp: # 从左边找比tmp大的数 left-=1 # left指向向右移动一个位置 li[right] = li[left] # 把左边的值写到右边空位上 li[left] = tmp # 把tmp归位 return left
4.快速排序的时间复杂度
快速排序的时间复杂度是O(nlogn)
5.快速排序存在的问题
1.最坏情况
如果要排序的列表是一个完全的倒序列表(比如[9,8,7,6,5,4,3,2,1]),这个情况是快速排序的最坏情况,时间复杂度为O(n²)。
2.递归
因为python的递归存在着最大递归次数(996-1000)次,而快速排序无法保证递归次数一定是在这个范围之内的。
8.堆排序
1.堆排序前戏之二叉树
- 二叉树:度不超过2的树
- 每个节点最多有两个孩子节点
- 两个孩子节点被区分为左孩子节点和右孩子节点
2.什么是满二叉树?
满二叉树:一个二叉树,如果每一个层的节点数都达到最大值,则这个二叉树就是满二叉树.
3.什么是完全二叉树?
完全二叉树:叶子节点只能出现在最下层和次下层,并且最下面一层的节点都集中在改成的最左边的若干位置的二叉树.
4.堆排序前戏之二叉树的存储方式
二叉树的存储方式(表示方式):
1.链式存储方式
2.顺序存储方式 (※)
5.二叉树的顺序存储方式
父节点和左孩子节点的编号下标有什么关系?
- 0-1 1-3 2-5 3-7 4-9
- i→2i+1
父节点和右孩子节点的编号下标有什么关系?
- 0-2 1-4 2-6 3-8 4-10
- i→2i+2
6.什么是堆?
堆:一种特殊的完全二叉树结构
- 大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大
- 小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小
7.堆的向下调整
8.堆排序的实现过程
1.建立堆。
2.得到堆顶元素,为最大元素
3.去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序
4.堆顶元素为第二大元素
5.重复步骤3,直到堆变空
9.堆的向下调整函数的实现
'''堆的向下调整函数''' def sift(li,low,high): """ li:列表 low:堆的根节点位置 high:堆的最后一个元素位置 """ i = low # i最开始的时候指向根节点 j = 2*i+1 # j开始的时候指向左孩子节点 tmp = li[low] # 把堆顶存起来 while j <= high: # 只要j位置有数 if j+1 <= high and li[j+1] > li[j]: # 如果右孩子节点存在,并且右孩子节点的值大于左孩子节点的值 j = j+1 # j指向右孩子节点 if li[j] > tmp: li[i] = li[j] i = j # 往下看一层 j = 2*i+1 else: # tmp更大,把tmp放到i的位置上 li[i] = tmp # 将tmp放回原位置i上(某一级领导位置上) break else: li[i] = tmp # 把tmp放到叶子节点上
10.堆排序函数的实现
def heap_sort(li): # 1.构造堆 n = len(li) for i in range(n-2//2,-1,-1): # i表示建堆的时候调整的部分的根的下标 sift(li,i,n-1) # 2.挨个出数 for i in range(n-1,-1,-1): # i指向当前堆的最后一个元素 li[0],li[i] = li[i],li[0] sift(li,0,i-1) # i-1是新的high
11.堆排序的时间复杂度
堆排序的时间复杂度是O(nlogn)。