高级算法日记4:查找与排序
来源:互联网 发布:jquery.form.js用法 编辑:程序博客网 时间:2024/05/17 06:41
作者:孙相国
E-mail: sunxiangguodut@qq.com
版权所有,严禁转载
- 查找
- 1 顺序查找
- 2 折半查找
- 3 分块查找
- 4 二叉排序树
- 41二叉排序树BST
- 42二叉排序树的性质
- 43 二叉排序树的查找
- 44 二叉排序树的插入
- 45 二叉排序树的删除
- 5 平衡二叉树
- 6 B树
- 7 哈希表查找
- 排序
- 0 排序前传
- 1 直接插入
- 2 折半插入
- 3 希尔排序
- 4 简单选择排序
- 5 堆排序
- 51 堆
- 52 堆的维护
- 53 建堆
- 54 堆排序
- 55 优先队列
- 56 习题
- 6 冒泡排序
- 7 快速排序
- 71 快速排序入门
- 72 快速排序进阶
- 8 计数排序
- 9 基数排序
- 10 桶排序
- 11 归并排序
1. 查找
1.1 顺序查找
弱智,不讲
成功情况下,平均查找长度:
时间复杂度:
不成功的情况下(即找的数不在表中),平均查找长度
1.2 折半查找
def bi_search(R,k): low,high=0,len(R) while low<=high: mid=(low+high)/2 #mid=low+(high-low)/2 if R[mid]==k: return mid if R[mid]>k: high=mid-1 else: low=mid+1 return -1
思考题:如何计算ASL?
判定树(page379)
时间复杂度为
不成功的查找长度为h,即
1.3 分块查找
![这里写图片描述](http://img.blog.csdn.net/20170825154849018?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2l0aHViXzM2MzI2OTU1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
若以折半查找索引表,以顺序查找块表,则
若以顺序查找查找索引表和块表则
当
上述3中查找方法的比较:
就平均查找长度而言,折半查找最小,分块次之,顺序最长
就表的结构而言,顺序查找对有序表无序表均适用,折半查找仅仅适用于有序表,分块查找要求表中元素至少是分块有序的。
就表的存储结构而言,顺序查找和分块查找可以用于顺序表和链表;而折半查找只用于顺序表
分块查找综合了顺序表和折半查找的优点。(既能较快地查找,又能适应动态变化的要求)
1.4 二叉排序树
1.4.1二叉排序树(BST)
- 若它左子树非空,则左子树上所有记录均小于根记录
- 若它右子树非空,则右子树上所有记录均大于根记录
- 左右子树本身又是一棵二叉排序树
1.4.2二叉排序树的性质
- 按照中序遍历得到的中序序列是一个递增有序序列
- 一棵二叉排序树中的最大节点是根节点的最右下节点,最小节点是根节点的最左下节点
- 只需给出一棵二叉排序树的先序序列/后序序列/层次序列中的一个,便可以唯一确定这棵二叉排序树,因为这些序列中所有元素的递增序列便是该二叉排序树的中序序列。
1.4.3 二叉排序树的查找
def search(root,key): if key==root: return root elif key<root: search(root.left_child,key) elif key>root: search(root.right_child,key) else: return none
一棵含有n个节点的二叉排序树高度为lgn-n
这也是查找所需要的时间复杂度
1.4.4 二叉排序树的插入
def insert(root,k): if root is none: root=k elif root>k: return insert(root.left_child,k) else: return insert(root.right_child,k)
1.4.5 二叉排序树的删除
二叉排序树的插入和删除操作时间复杂度均为
1.5 平衡二叉树
1.6 B树
1.7 哈希表查找
2. 排序
2.0 排序前传
各种排序算法的江湖地位:
傻子系列:
直接插入(弱智)
折半插入(自以为漂亮的弱智)
希尔排序(自以为高大上的弱智)
不会你都系列:
冒泡排序(不会你都不好意思思考人生)
计数排序(不会你都不好意思吹牛逼)
基数排序(不会你都不好意思撩妹)
桶排序(不会你都不好意思装大神)
核心系列:
简单选择排序(重要)
堆排序(三大金刚之一,非常重要!)
快速排序 (三大金刚之二,非常重要的平方!!)
归并排序(三大金刚之三,非常重要)
边缘:
外排序(屌丝)
2.1 直接插入
void insert_sort(sqlist R[], int n){ int i,j; sqlist temp; for (i=1;i<n;i++) { temp=R[i]; j=i-1; while(j>=0 && temp.key<R[j].key) { R[j+1]=R[j]; j--; } R[j+1]=temp; }}
def insert_sort(r): for i in range(0,len(r)): pos=i while r[pos]>r[i]: pos=pos-1 r[pos:i+1]=r[i]+r[pos:i]
globally sequential
no
time complexity
average:
worst:
best:
space complexity
stability
stable
2.2 折半插入
void insert_sort(sqlist R[], int n){ int i,j,low,high,mid sqlist temp; for (i=1;i<n;i++) { temp=R[i]; low=0;high=i-1; while(low<high) { mid=(low+high)/2; if(temp.key<R[mid].key) high=mid-1; else low=mid+1 } for (j=i-1;j>=high+1;j--) { R[j+1]=R[j]; } R[high+1]=temp; }}
def insert_sort(r): for i in range(0,len(r)): low,high=0,i-1 while low<=high: if r[i]<r[mid]: high=high+1 else: low=mid+1 r[high+1:i+1]=r[i]+r[high+1:i]
globally sequential
no
time complexity
average:
worst:
best:
space complexity
stability
stable
2.3 希尔排序
void shell_sort(sqlist R[], int n){ int i,j,d; sqlist temp; d=n/2; while(d>0) { for (i=d;i<n;i++) { temp=R[i]; j=i-d; while(j>=0 &&temp.key<R[j].key) { R[j+d]=R[j]; j=j-d; } R[j+d]=temp; } d=d/2; }}
def insert_sort(r): for i in range(0,len(r)): low,high=0,i-1 while low<=high: if r[i]<r[mid]: high=high+1 else: low=mid+1 r[high+1:i+1]=r[i]+r[high+1:i]def shell_sort(r): d=len(r)/2 while d>0: for i in range(0,d): insert_sort(r[range(i,len(r),d)]) d=d/2
globally sequential
no
不考
time complexity
average:
space complexity
stability
unstable
希尔排序的时间复杂度与子排序算法,增量策略,有极大的关系。没有定论
http://blog.csdn.net/u013630349/article/details/48250109
2.4 简单选择排序
void select_sort(sqlist R[], int n){ int i,j,k; sqlist temp; for (i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(R[j].key<R[k].key) k=j; swap(R[i],R[k]); /* if(i!=k) { temp=R[i]; R[i]=R[k]; R[k]=temp; } */ }}
def select_sort(r): for i in range(0,len(r)): k=argmin(r[i+1:]) r[i],r[k]=r[k],r[i]
def select_sort(r): for i in range(0,len(r)): r[i],r[argmin(r[i+1:])]=r[argmin(r[i+1:])],r[i]
globally sequential
yes
time complexity
average:
worst:
best:
space complexity
stability
unstable
2.5 堆排序
2.5.1 堆
堆,一般指的是二叉堆,这是一个数组,可以看做一个完全二叉树,如下图所示:
二叉堆可以分为两种形式:最大堆和最小堆
最大堆:
最小堆:
堆排序算法中,默认使用的是最大堆,而最小堆通常用于构造优先队列
复习:n个节点的完全二叉树,高度是:
以后为了方便起见,简称为
需要重点掌握的操作有(达到精通代码的程度):
- max_heapify: 维护最大堆
- build_max_heap: 构建最大堆
- heap_sort:堆排序算法
- 其他重要的堆操作(用于实现优先队列)有:max_heap_insert; heap_extract_max; heap_increase_key; heap_maxmum
2.5.2 堆的维护
什么叫做堆的维护?(max_heapify)
对于一个二叉堆,假设以root.left
和root.right
为根的二叉堆分别都是最大堆,但是root
这个值却没有校验,即,我们不知道是否以root
为根的二叉堆也为一个最大堆。最大堆维护,就是希望将此时的root
放到正确的位置,从而使得以root
为根的二叉堆也为一个最大堆。
def max_heapify(A,i): left=2*i right=if 2*i+1>len(A) none else 2*i+1 """ method1(sunsum): if A[left]>A[i]: largest=l else: largest=i if A[right]>A[largest]: largest=r method2(xiaomi): largest=argmax([A[left],A[i],A[right]]) if largest==0: largest=left elif largest==1: largest=i elif largest==2: largest=right method3(iPhone): """ largest=[left,i,right] largest=largest[argmax([A[left],A[i],A[right]])] if largest!=i: swap(A[i],A[largest]) max_heapity(A,largest)
C++代码与上面的代码雷同,略。
def max_heapify(A,current_root): largest=max(current_root.left,current_root,current_root.right) if largest is not current_root: swap(largest,current_root) max_heapify(A,largest)
思考题(家庭作业):如何采用非递归的方式实现?
void max_heapify(sqlist R[],int low,int high){ int i=low, j=2*i; sqlist temp=R[i]; while(j<=high) { if (j<high && R[j]<R[j+1]) j++; if(temp<R[j]) { R[i]=R[j]; i=j; j=2*i; } else break; } R[i]=temp;}
2.5.3 建堆
def build_max_heap(A): for i in range(len(A)/2,0,-1): max_heapify(A,i)
2.5.4 堆排序
def heap_sort(A): for i in range(0,len(A)): build_max_heap(A[i:]) swap(A[i],A[-1])
globally sequential
yes
time complexity
average:
worst:
best:
space complexity
stability
unstable
2.5.5 优先队列
优先队列(priority queue) 是一种用来维护一组元素构成的集合S的数据结构,其中每一个元素都有一个key(关键字)。一个最大优先队列支持以下操作:
- insert(S,x): 把元素x插入集合S中。这一操作等价于
S=S∪x (回顾之前DFS操作中的T=T∪(u,v) ) - maximum(S): 返回S中具有最大键字的元素
- extract_max(S): 去掉并返回S中具有最大键字的元素
- increase_key(S,x,k): 将元素x的关键字增加到k,这里假设k的值不小于x的原关键字。
优先队列的应用:
1,最大优先队列,用于操作系统中的作业调度。最大优先队列记录将要执行的各个作业以及它们之间的相对优先级。当一个作业完成或者中断后,调度器调用extract_max从所有等待的作业中,选出一个具有最高优先级的作业来执行。在任何时候,调度器都可以调用insert把一个新作业加入到队列中来。
2,最小优先队列,亲爱的,还记得我们之前讲过的最下生成树吗?记不记得那个prim算法的复杂度?里面有一个操作,叫做extract_min()?是的,你没看错,就是这里面的东西。
这部分的代码不需要掌握,了解即可。
用堆来实现优先队列
def heap_maximum(S): return S[0]
def heap_extract_max(S): max=S[0] S[0]=S[-1] max_heapity(S,0) return max
时间复杂度是
这就是说,如果采用堆来实现,那么先前我们的prim算法复杂度可以进一步降低为
def heap_increase_key(S,i,key): A[i]=key while i>0 and A[parent(i)]<A[i]: swap(A[i],A[parent(i)]) i=parent(i)
def heap_insert(A,key): A=A+(key-1) heap_increass_key(A,len(A),key)
2.5.6 习题
2.6 冒泡排序
def bubble_sort(r): for i in range(0,len(r)): for j in range(len(r),i,-1): if r[j]<r[j-1]: swap(r[j],r[j-1])
设置exchange标记,使得算法提前终止
def bubble_sort(r): for i in range(0,len(r)): exchange = False for j in range(len(r),i,-1): if r[j]<r[j-1]: swap(r[j],r[j-1]) exchange=True if exchange == False: return
void bubble_sort(sqlist R[], int n){ int i,j,exchange; sqlist temp; for (i=0;i<n-1;i++) { exchange = 0; for (j=n-1;j>i;j--) if (R[j]<R[j-1]) { temp = R[j]; R[j]=R[j-1]; R[j-1]=temp; exchange=1; } if (exchange==0) return; }}
globally sequential
yes
time complexity
average:
worst:
best:
space complexity
stability
stable
2.7 快速排序
2.7.1 快速排序入门
如何把一个数组分词大小两半?
把
def partition(A,p,r): x = A[r] i=p-1 for j in range(p,r): if A[j]<=x: i=i+1 swap(A[i],A[j]) swap(A[i+1],A[r]) return i+1
快速排序
def quick_sort(A,p,r): if p < r: q = partition(A,p,r) quick_sort(A,p,q-1) quick_sort(A,q+1,r)
C++
void quick_sort(sqlist R[], int s, int t){ int i=s,j=t; sqlist temp; if (s<t) { temp = R[s]; while(i!=j) { while(j>i && R[j]>temp) j--; R[i] = R[j]; while(i<j && R[i]<temp) i++; R[j] = R[i]; } R[i]=temp; quick_sort(R,s,i-1); quick_sort(R,i+1,t); }}
globally sequential
no
但是每一趟都会归为一个元素(即,每一趟,都会确定一个元素的最终位置)
time complexity
average:
worst:
best:
space complexity
stability
unstable
2.7.2 快速排序进阶
性能分析
随机化
最坏情况分析
期望运行时间
2.8 计数排序
使用场景:假设n个输入元素的每一个都是在0-k区间内的一个整数,其中k为某个整数。
定义3个数组,A[1:n]为待排序数组。B[1:n]为存放排序的输出。C[0:k]为临时空间。
def count_sort(A,B,k): C=[0 for i in range(0,k)] for i in range(0,k+1): C[i]=A.count(i) for i in range(1,k+1): C[i] = C[i]+C[i-1] for j in range(len(A),0,-1): B[C[A[j]]] = A[j] C[A[j]] = C[A[j]]-1
time complexity
当
space complexity
stability
stable
2.9 基数排序
无需掌握代码
globally sequential
no
time complexity
n个d位数,每一个数位有r个可能的取值,则
average:
worst:
best:
space complexity
stability
stable
2.10 桶排序
了解即可
假设输入数据服从均匀分布,桶排序将[0,1)区间划分为n个大小相同的子区间,称为桶。然后将n个输入数据分别放到各个桶子中,对桶内的数据分别进行排序,然后将桶子连接。
2.11 归并排序
首先,将R[0:n-1]看成是n个长度为1的有序表,将相邻的有序表成对归并(将多个有序表组成一个新的有序表叫做归并),得到
由于上述的归并是对相邻的两个有序表进行的,因此叫做二路归并。如果归并操作在相邻的多个有序表中进行,则叫做多路归并排序。默认情况下所说的归并排序,指的是二路归并排序,如下图:
void merge(sqlist R[], int low, int mid, int high){ sqlist R1; int i=low,j=mid+1; while (i<=mid &&j<=high) if (R[i]<=R[j]) { R1.append(R[i]); i++; } else { R1.append(R[j]); j++; } while (i<=mid) { R1.append(R[i]); i++; } while (j<=high) { R1.append(R[j]); j++; } R.erase() R=R1;}
void merge_pass(sqlist R[], int length, int n){ for(i=0;i+2*length-1<n;i=i+2*lengtgh) merge(R,i,i+length-1,i+2*length-1); if (i+length-1<n) merge(R,i,i+length-1,n-1)}
void merge_sort(sqlist R[],int n){ int length; for (length=1;length<n;length=2*length) merge_pass(R,length,n);}
globally sequential
no
time complexity
average:
worst:
best:
space complexity
stability
stable
- 高级算法日记4:查找与排序
- 位图算法排序与查找
- 算法学习--排序与查找
- 算法基础:排序与查找
- 高级算法日记11:图(4)
- 数据结构与算法--查找与排序
- Java数据结构与算法--高级排序
- .net 数据结构与算法基础:高级排序
- java数据结构与算法-高级排序-基数排序
- python数据结构与算法27 排序与查找 顺序查找
- 二分查找算法与快速排序
- 常用的查找与排序算法
- 排序与查找的最好算法
- 算法笔记之常用查找与排序
- 数据结构与算法练习-数组查找,排序
- 学习笔记---排序与查找的算法
- --1 排序与查找 基础算法
- 高级算法日记7:专题
- Linux下 文件权限与文件类型
- Java之网络编程(一)
- Mybatis-宏观学习
- Unity3D之AssetBundle【1】AssetBundle流程及原理
- Android 加载网络图片并做缓存
- 高级算法日记4:查找与排序
- Mesh---mesh(fbx)的获取
- 即时通讯
- sql防止注入 like 和 in 应该怎么写
- 【秒懂】号称最为简明实用的Django上手教程
- JavaScript 字符串函数学习脑图
- Android 7.0 系统解决拍照的问题 exposed beyond app through ClipData.Item.getUri()
- Lambda基础例子
- 基于LoRa的物联网技术分析