如何备考数据结构大题
数据结构的小题最好的复习方法是把王道书的课后习题二刷
多做题少听课
主动发现自己不足的地方,去查缺补漏而不是遍历课本
大题的题型
应用题侧重于算法的逻辑而不是算法代码的实现
大纲里面应用相关即大题要考的考点
应用题备考思路:
画数据结构和算法运行过程
训练数据结构定义的简单代码
基本操作的简单代码
八大考点
①线性表的应用
②栈的应用
③队列的应用
④数组的应用
⑤树与二叉树的应用
二叉树基本考的就是哈夫曼树和哈夫曼编码,并查集及其应用。其他的题型通常是定义数据结构或者实现基本操作
⑥图的基本应用
比较基本的应用
应用题一般不用写代码但是要手算,能够懂这些算法运行的逻辑
最小(代价)生成树
最小生成树考察的是一个图中包含多个点的总路线代价最短
最短路径
拓扑排序
关键路径
其他
⑦查找算法的分析及应用
⑧排序算法的分析及应用
七大考法
①画图作答
②代码作答
(1)写数据结构定义、基本操作代码
简单的代码
基本操作/功能的代码片段,比如判断队空或者插入
比较复杂的数据结构不会考很复杂的算法题,但可能考数据结构的代码定义以及基本操作的代码实现
③文字简答
(1)手算分析算法运行过程/结果
(2)数据结构、算法的选择
基于应用场景选择一种合适的数据结构
基于应用场景选择一种性能比较好的算法
(3)算法性质分析
分析算法的时间复杂度和空间复杂度
查找算法
排序算法
分析关键字的对比次数
某一趟排序当中关键字的对比次数是多少
排完序关键字的总对比次数是多少
稳定性
(4)文字描述算法思想/过程
可以通过示意图去解释算法的运行过程,用文字描述也可以解和画图去做
将画数据结构的状态示意图和手算分析算法运行过程/结果结合起来,从这两个角度去把各个算法的运行过程和数据结构的示意图捋清楚
(5)数据结构性质推演
应用题
排序类应用题
插入类排序:直接插入排序、折半插入排序、希尔排序
能够保证相对升序的序列(有可能是跳跃的,也有可能是前面的i+1个
选择类排序:简单选择排序、堆排序
堆排序:(大根堆)每次选择堆顶元素放在最后(和堆底元素交换),每次将最大的元素放在最后面
第一趟排序是先顺序存进一个二叉树,调整成为一个大根堆,再把它那个最大的元素换到最后面,再次调整为大根堆的结果
每次都有一个元素被放在最终位置
交换类排序:冒泡排序、快速排序
不稳定排序算法的特点
对于一种排序算法,我们要交换前两个位置的时候,如果它看不到前面有一个和他相同的元素,就有可能出现不稳定
查找类应用题
折半查找
散列查找查找失败的情况
线性探测法,查找到空位的时候说明查找失败,比较空的数组元素会计入一次比较次数
链地址法,比较到一个空指针的位置才是查找失败,但是该比较空指针不会算进比较次数
装填因子在链地址法中可以大于1,但是存储效率一定是小于等于100%的
查找成功的时候分母是元素数量,查找失败的时候分母是取模后面的那个数字(因为无法映射到其他更大的数字)
链式存储,考虑二叉搜索树
顺序存储,顺序查找和折半查找(注意排序)比较常见
树类应用题
图类应用题
线性表类应用题
算法题
算法题
一题多解,分数高中低三档
可以先写第二小问再写第一小问
顺序表和链表一般情况下要考察时间复杂度
树和图一般不考时间复杂度和空间复杂度,分析要求低一点,但依旧要记得
树和图能解决问题比高效解决问题最重要,只要能解决问题笨方法也可以,只要能掌握常规代码即可
备考方法
阶段一:完成历年真题
阶段二:分模块训练
阶段三:少食多餐,增加做题量
阶段四:考前保持手感,再刷历年真题
排序
顺序表
链表
不支持随机访问,算法较少
考察偏向于考基本功
按位序查找
按关键字条件查找+删除某个节点
按关键字条件查找+插入某个节点
头插法(可实现原地逆置)
尾插法(保持原序)
树
掌握前中后序遍历、层序遍历、递归算法
非递归算法不用掌握
基于这些算法解决思维导图中列出的问题,然后做一下王道书的剩余算法题
图
数据结构层面
邻接矩阵和邻接表的遍历
但是邻接表只能bfs或者dfs
邻接多重表和十字链表的定义
算法备考
1 2 3 4 5 6 7 8 9 10 11 12 int stk[N], tt = 0 ; stk[ ++ tt] = x; tt -- ; stk[tt];
C++
// 判断栈是否为空,如果 tt > 0,则表示不为空
if (tt > 0) not empty;
else empty;
//栈顶
stk[tt];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ```c++int q[N], hh = 0 , tt = -1 ; q[ ++ tt] = x; hh ++ ; q[hh]; q[tt];if (hh <= tt) not empty ; else empty ;
PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int q[N], hh = 0 , tt = 0 ; q[tt ++ ] = x; if (tt == N) tt = 0 ; hh ++ ; if (hh == N) hh = 0 ; q[hh]; if (hh != tt) { }
C++
1 2 3 4 5 6 7 8 9 10 11 bool st[N];int dfs (int u) { st[u] = true ; for (int i = h[u]; i != -1 ; i = ne[i]) { int j = e[i]; if (!st[j]) dfs (j); } }
C++
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 queue<int > q; st[1 ] = true ; q.push (1 );while (q.size ()) { int t = q.front (); q.pop (); for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; if (!st[j]) { st[j] = true ; q.push (j); } } }int bfs () { int hh =0 ,tt=0 ; q[0 ]=1 ; memset (d,-1 ,sizeof d); d[1 ]=0 ; while (hh<=tt){ int t=q[hh++] for (int i = h[t];i!=-1 ;i=e[i]) { int j =e[i]; if (d[j]==-1 ) { d[j]=d[t]+1 ; q[++tt]=j; } } } return 0 ; }
C++
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 int n,m;int h[N],e[N],ne[N],idx;int d[N],q[N];void add (int a,int b) { e[idx] = b; ne[idx] = h[a]; h[a]=idx++; }bool topsort () { int hh = 0 , tt = -1 ; for (int i = 1 ; i <= n; i ++ ) if (!d[i]) q[ ++ tt] = i; while (hh <= tt) { int t = q[hh ++ ]; for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; d[j]--; if ( d[j] == 0 ) q[ ++ tt] = j; } } return tt == n - 1 ; }
C++
一休的算法题建议
评分细则
顺序表、链表注意复杂度
树、图注意细节
序列就是数组
答题技巧
顺序表和链表
暴力解
其他解
目标:尽量优化自己的算法,让自己的复杂度变低
树
核心是遍历(递归)
实际上王道书上的树的递归算法可以跳过,因为不同的先序、后序、中序算法都是不一样的,还有不一样的写法。
如果递归的话,先中后逻辑都是一样的,实际上只有三行核心代码
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 void preorder (btnode *p) { if (p==NULL ){ return ; visit (p); preorder (p->lchild); preorder (p->rchild); } }void inorder (btnode *p) { if (p==NULL ) return ; inorder (p->lchild); visit (p); inorder (p->rchild); }void postorder (btnode *p) { if (p==NULL ) return ; postorder (p->lchild); postorder (p->rchild); visit (p); }
C++
图
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 int n; int g[N][N]; int dist[N]; bool st[N]; int prim () { memset (dist, 0x3f , sizeof dist); int res = 0 ; for (int i = 0 ; i < n; i ++ ) { int t = -1 ; for (int j = 1 ; j <= n; j ++ ) if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j; if (i && dist[t] == INF) return INF; if (i) res += dist[t]; st[t] = true ; for (int j = 1 ; j <= n; j ++ ) dist[j] = min (dist[j], g[t][j]); } return res; }
C++
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 int n, m; int p[N]; struct Edge { int a, b, w; bool operator < (const Edge &W)const { return w < W.w; } }edges[M];int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; }int kruskal () { sort (edges, edges + m); for (int i = 1 ; i <= n; i ++ ) p[i] = i; int res = 0 , cnt = 0 ; for (int i = 0 ; i < m; i ++ ) { int a = edges[i].a, b = edges[i].b, w = edges[i].w; a = find (a), b = find (b); if (a != b) { p[a] = b; res += w; cnt ++ ; } } if (cnt < n - 1 ) return INF; return res; }
C++
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 int g[N][N]; int dist[N]; bool st[N]; int dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; for (int i = 0 ; i < n - 1 ; i ++ ) { int t = -1 ; for (int j = 1 ; j <= n; j ++ ) if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j; for (int j = 1 ; j <= n; j ++ ) dist[j] = min (dist[j], dist[t] + g[t][j]); st[t] = true ; } if (dist[n] == 0x3f3f3f3f ) return -1 ; return dist[n]; } cin >> n>>m;memset (g,0x3f ,sizeof g);while (m--){ int a,b,c; cin>>a>>b>>c; g[a][b]=min (g[a][b],c); }int t =dijkstra ()l cout<<t;return 0 ;
C++
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 typedef pair<int , int > PII;int n; int h[N], w[N], e[N], ne[N], idx; int dist[N]; bool st[N]; void add (int a,int b,int c) { e[idx]=b,w[idx]= c,ne[idx]=h[a],h[a]=idx++; }int dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push ({0 , 1 }); while (heap.size ()) { auto t = heap.top (); heap.pop (); int ver = t.second, distance = t.first; if (st[ver]) continue ; st[ver] = true ; for (int i = h[ver]; i != -1 ; i = ne[i]) { int j = e[i]; if (dist[j] > distance + w[i]) { dist[j] = distance + w[i]; heap.push ({dist[j], j}); } } } if (dist[n] == 0x3f3f3f3f ) return -1 ; return dist[n]; }int main () { cin >> n>>m; memset (h,-1 ,sizeof h); while (m--){ int a,b,c; cin >> a >> b>>c; add (a,b,c); } int t = dijkstra (); cout<<t; return 0 ; }
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 cosnt int INF=1e9 ; 初始化: for (int i = 1 ; i <= n; i ++ ) for (int j = 1 ; j <= n; j ++ ) if (i == j) d[i][j] = 0 ; else d[i][j] = INF;void floyd () { for (int k = 1 ; k <= n; k ++ ) for (int i = 1 ; i <= n; i ++ ) for (int j = 1 ; j <= n; j ++ ) d[i][j] = min (d[i][j], d[i][k] + d[k][j]); }
C++
能不能直接调用函数
1 2 3 4 5 6 pop ();push ();swap (a,b);abs ();pow (x,y);sqrt ();
C++
过程
1 2 3 4 5 6 7 8 int binary () { }int ans (int s[],int n ,int k) { }
C++
空间复杂度的分析
申请了哪些数组
申请了哪些结点
递归的时候有多少层的递归(树的高度,递归的层数)
栈、队列的一些代码模板
数组实现栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int stk[N],tt=0 ; stk[++tt]=x; x=stk[tt]; x=stk[tt--];if (tt==0 )if (tt==maxsize)
C++
链表实现栈
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 struct Lnode { int data; Lnode* next; };using listack=Lnode*;bool initstack (listack &s) { s=new Lnode; s->next=nullptr ; return true ; }bool isempty (listack s) { if (s->next==nullptr ) return true ; else return false ; }bool push (listack &s,int x) { Lnode *p=new Lnode; p->data= x; p->next=s->next; s->next=p; return true ; }bool pop (listack &s,int &x) { if (isempty (s)) return false ; Lnode *p=s->next; x=p->data; s->next=p->next; delete p; return true ; }
C++
双向链表实现栈(栈顶在链尾)
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 struct dnode { int data; dnode*pre,*next; };struct dstack { dnode *head,*rear; };using stk=dstack*;bool init (stk &s) { s=new dstack; dnode *p=new dnode; p->next=nullptr ; p->pre=nullptr ; s->head=p; s->rear=p; return true ; }bool isempty (stk s) { if (s->head==s->rear) return true ; else return false ; }bool push (stk &s,int x) { dnode *p=new dnode; p->data=x; p->next=nullptr ; p->pre=s->rear; s->rear->next=p; s->rear=p; return true ; }bool pop (stk &s,int &x) { if (isempty (s)) return false ; dnode *p=s->rear; x=p->data; s->rear=p->pre; s->rear->next=nullptr ; delete p; return true ; }
C++
数组实现循环队列
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 const int maxsize =100 ;struct queue { int data[maxsize]; int rear,head; };void init (queue &q) { q.rear=q.head=0 ; }bool isempty (queue q) { if (q.head==q.rear) return true ; else return false ; }bool isfull (queue q) { if ((q.rear+1 )%maxsize==q.head) return true ; else return false ; }bool inqueue (queue &q,int x) { if (isfull (q))return false ; q.data[q.rear]=x; q.rear=(q.rear+1 )%maxsize; return true ; }bool outqueue (queue &q,int &x) { if (isempty (q)) return false ; x=q.data[q.head]; q.head=(q.head+1 )%maxsize; return true ; }
C++
单链表实现队列
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 struct qnode { int data; qnode *next; };struct listqueue { qnode*head,*rear; };using lq=listqueue*;bool init (lq &q) { q=new listqueue; qnode *p=new qnode; q->head=p; q->rear=p; p->next=nullptr ; return true ; }bool isempty (lq q) { return q->head==q->rear; }bool inqueue (lq &q,int x) { qnode *p=new qnode; p->data=x; p->next=nullptr ; q->rear->next=p; q->rear=p; return true ; }bool outqueue (lq &q,int &x) { if (isempty (q))return false ; qnode *p=q->head->next; x=p->data; q->head->next=p->next; if (q->head->next==nullptr ) q->rear=q->head; delete p; return true ; }
C++
树的一些代码模板
数组实现二叉树
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 struct treenode { int data; bool empty; };void init (treenode t[],int length) { for (int i = 0 ;i<length;i++) t[i].empty=true ; }int main () { treenode t[100 ]; init (t,100 ); }bool isempty (treenode t[],int length,int x) { if (x>=length || x<1 ) return true ; return t[x].empty; }int findlkid (treenode t[],int length,int x) { int lkid = x*2 ; if (isempty (t,length,lkid))return -1 ; return lkid; }int findrkid (treenode t[],int length,int x) { int rkid = x*2 +1 ; if (isempty (t,length,rkid))return -1 ; return rkid; }int findparent (treenode t[],int length,int x) { if (x==1 )return -1 ; int parent=x/2 ; if (isempty (t,length,parent))return -1 ; return parent; }
C++
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 struct treenode { int data; bool empty; };void init (treenode t[],int length) { for (int i= 0 ;i<length;i++) t[i].empty=true ; }int main () { treenode t[100 ]; init (t,100 ); return 0 ; }bool isempty (treenode t[],int length,int x) { if (x<0 || x>=length) return true ; return t[x].empty; }int findparent (treenode t[],int length,int x) { if (x==0 )return -1 ; int parent=(x-1 )>>1 ; if (isempty (t,length,parent))return -1 ; return parent; }int findlkid (treenode t[],int length,int x) { int lkid = x*2 +1 ; if (isempty (t,length,lkid))return -1 ; return lkid; }int findrkid (treenode t[],int length,int x) { int rkid=x*2 +2 ; if (isempty (t,length,rkid))return -1 ; return rkid; }
C++
二叉树的先中后序遍历
先序遍历
1 2 3 4 5 6 7 void visitnode (treenode &x) ;void preorder (treenode t[],int length,int x) { if (hisempty (t,lengt,x))return ; visitnode (t[x]); preorder (t,length,getlkid (t,length,x)); preorder (t,length,getrkid (t,length,x)); }
C++
中序遍历
1 2 3 4 5 6 7 void visitnode (treenode &x) ;void inorder (treenode t[],int length,int x) { if (isempty (t,length,x)) return ; inorder (t,length,getlkid (t,length,x)); visitnode (t[x]); inorder (t,length,getrkid (t,length,x)); }
C++
后序遍历
1 2 3 4 5 6 7 void visitnode (treenode &x) ;void postorder (treenode t[],int length,int x) { if (isempty (t,length,x)) return ; postorder (t,length,getlkid (t,length,x)); postorder (t,length,getrkid (t,length,x)); visitnode (t[x]); }
C++
树的存储结构
双亲表示法
1 2 3 4 5 6 7 8 9 10 11 const int maxsize=100 ;struct ptnode { int data; int parent; };struct ptree { ptnode nodes[maxsize]; int n; };
C++
孩子表示法
1 2 3 4 5 6 7 8 9 10 11 const int maxsize=100 ;struct kidnode { int index; kidnode *next; };struct parentnode { int data; kidnoe *firstkid; }tree[maxsize];
C++
孩子兄弟表示法
1 2 3 4 5 6 struct node { int data; node*firstkid,*nextbrother; };using tree=node*;
C++
并查集
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 const int N=100 ;int size[N],p[N];void init (int size[],int p[],int length) { for (int i= 1 ;i<=length;i++) { size[i]=1 ; p[i]=i; } }int find (int x) { if (p[x]!=x) p[x]=find (p[x]); return p[x]; }void merge (int a,int b) { int r1=find (a); int r2=find (b); if (r1==r2)return ; if (size[r1]>=size[r2]){ p[r2]=r1; size[r1]+=size[r2]; } else { p[r1]=r2; size[r2]+=size[r1]; } }
C++
图的一些代码模板
邻接矩阵实现图的顺序存储
1 2 3 4 5 6 7 const int N =100 ;struct graph { int n,e; char vex[N]; int weight[N][N]; };
C++
邻接表实现图的链式存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const int N=100 ;struct arcnode { int adjvex; arcnode*nextarc; int info; };struct vnode { char data; arcnode *firstarc; }struct graph { vnode adjlist[N]; int n,e; };
C++
dijkstra算法的文字过程描述
bfs算法的文字过程描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 A / \ B - C | | D - E \ F
C++
拓扑排序的文字过程描述
关键长度的意义
朴素版prim算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const int N=1000 ;int n;int g[N][N];int dist[N];bool st[N];int prim () { int res=0 ; memset (dist,0x3f ,sizeof (dist)); for (int i = 0 ;i<n;i++){ int t=-1 ; for (int j = 1 ;j<=n;j++) if (!st[j]&&(t==-1 || dist[t]>dist[j])) t=j; if (i && dist[t]==INF)return INF; if (i) res+=dist[t]; st[t]=true ; for (int j = 1 ;j<=n;j++) dist[j]=min (dist[j],g[t][j]); } return res; }
C++
排序的一些代码模板
快速排序
1 2 3 4 5 6 7 8 9 10 void quick_sort (int q[],int l,int r) { if (l>=r)return ; int i = l-1 ,j=r+1 ,x=q[l+r>>1 ] while (i<j){ do i++;while (q[i]<x); do j--;while (q[j]>x); if (i<j)swap (q[i],q[j]); } quick_sort (q,l,j),quick_sort (q,j+1 ,r); }
C++
归并排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void merge_sort (int q[],int l,int r) { if (l>=r)return ; merge_sort (q,l,mid); merge_sort (q,mid+1 ,r); int k = 0 ,i = l , j = mid+1 ; while (i<=mid && j <=r){ if (q[i]<=q[j])tmp[k++]=q[i++]; else tmp[k++]=q[j++]; } while (i<=mid)tmp[k++]=q[i++]; while (j<=r)tmp[k++]=q[j++]; for (j = 0 ,i=l;i<=r;i++,j++) q[i]=tmp[j]; }
C++
查找的一些代码模板
二分查找
错题本
2010 线性表 三次翻转
1 2 3 4 5 6 7 8 9 10 void moveleft (int q[],int p,int n) { int tmp[n]; for (int i = 0 ;i<n;i++) tmp[(i+n-p)%n]=q[i]; for (int i = 0 ;i<n;i++) q[i]=tmp[i]; }
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void reverse (int q[],int l,int r) { if (l>=r)return ; while (l<r){ swap (q[l],q[r]); l++,r--; } }void moveleft (int q[],int n,int p) { reverse (q,0 ,p-1 ); reverse (q,p,n-1 ); reverse (q,0 ,n-1 ); }
C++
2011 线性表 二分搜索
1 2 3 4 5 6 7 8 9 10 11 12 int findmid (int a[],int b[],int l) { int i = 0 ,j=0 ; while (i+j<l-1 ){ if (a[i]>=b[j])j++; else i++; } return min (a[i],b[j]); }
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int findMid (int a[], int b[], int n) { int l = 0 , r = n; while (l < r) { int i = (l + r) / 2 ; int j = n - i; if (i < n && b[j - 1 ] > a[i]) l = i + 1 ; else if (i > 0 && a[i - 1 ] > b[j]) r = i - 1 ; else { int leftMax = 0 ; if (i == 0 ) leftMax = b[j - 1 ]; else if (j == 0 ) leftMax = a[i - 1 ]; else leftMax = std::max (a[i - 1 ], b[j - 1 ]); return leftMax; } } int i = l, j = n - l; if (i == 0 ) return b[j - 1 ]; if (j == 0 ) return a[i - 1 ]; return std::max (a[i - 1 ], b[j - 1 ]); }
C++
2013 线性表 摩尔投票法
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 void quick_sort (int q[],int l,int r) { if (l>=r)return ; int x=q[l+r>>1 ],i=l-1 ,j=r+1 ; while (i<j){ do i++;while (q[i]<x); do j--;while (q[j]>x); if (i<j)swap (q[i],q[j]); } quick_sort (q,l,j),quick_sort (q,j+1 ,r); }int bsl (int l,int r,int x,int q[]) { while (l<r){ int mid=l+r+1 >>1 ; if (q[mid]<x)l=mid; else r=mid-1 ; } return l; }int bsh (int l,int r,int x,int q[]) { while (l<r){ int mid = l+r>>1 ; if (q[mid]>x)r=mid; else l = mid+1 ; } return l; }int findmain (int a[],int n) { quick_sort (a,0 ,n-1 ); int result = a[n-1 >>1 ]; int left=bsl (0 ,n-1 ,result,a); int right=bsh (0 ,n-1 ,result,a); if (right-left-1 >(n>>1 ))return result; else return -1 ; }
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int find_main (int a[], int n) { int candidate = -1 , count = 0 ; for (int i = 0 ; i < n; i++) { if (count == 0 ) { candidate = a[i]; count = 1 ; } else if (a[i] == candidate) { count++; } else { count--; } } count = 0 ; for (int i = 0 ; i < n; i++) { if (a[i] == candidate) count++; } return (count > n / 2 ) ? candidate : -1 ; }
C++
🎭 这个算法其实是在模拟“投票大战”!
想象你现在有一群人,每人代表一个数。你的目标是找出 有没有某个数在这群人里占多数(超过一半) 。
🥊 摩尔投票的逻辑是:
你一开始不知道谁是主角,所以从第一个人开始猜;
如果后来遇到和当前猜测一样的人(站你这边),你就信心 +1;
如果遇到和你猜的不一样的人(反对你),你就信心 -1;
如果信心掉到 0,就说明你现在这个候选人支持和反对打平了,需要换人重新开始;
最后剩下来的“赢家”有可能是主角,但得再数一遍确认一下它是不是真的超过一半。
🎯 那为什么 count--
是必要的?
因为每遇到一个“不同”的数,就是“主元素”的一个反对票。
如果主元素真的超过一半,那其他所有数都不够把它抵消掉,它一定能撑到最后!
比如数组:
1 2 3 4 5 csharp 复制编辑[5, 5, 3, 5, 7, 5, 5, 1]
ACCESSLOG
主元素是 5,它有 5 票;
剩下的 3、7、1 只有 3 票;
即使这 3 票全都拿来反对 5,5 还是赢了!
所以你在每遇到一个“异类”时,就等于主角被反对了一次,要 count--
。
✅ 总结一句话:
count--
是为了让反对的声音抵消掉主元素的票数,看它能不能最终剩下来。
2018 线性表 原地哈希法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void quick_sort (int a[],int l,int r) { if (l>=r)return ; int x=a[l+r>>1 ],i=l-1 ,j=r+1 ; while (i<j){ do i++ ;while (a[i]<x); do j--;while (a[j]>x); if (i<j)swap (a[i],a[j]); } quick_sort (a,l,j),quick_sort (a,j+1 ,r); }int findmin (int a[],int n) { int mini = 1 ; quick_sort (a,0 ,n-1 ); for (int i = 0 ; i<n;i++){ if (a[i]==mini) mini++; } return mini; }
C++
1 2 3 4 5 6 7 8 9 10 11 int findmin (int a[],int n) { int tmp[n]; memset (tmp,0 ,sizeof (tmp)); for (int i = 0 ;i<n;i++){ if (a[i]>0 &&a[i]<=n)tmp[a[i]-1 ]=1 ; } for (int i = 0 ;i<n;i++) if (!tmp[i])return i+1 ; }
C++
1 2 3 4 5 6 7 8 int findmin (int a[],int n) { for (int i = 0 ;i<n;i++) while (a[i]>0 && a[i]<=n &&a[a[i]-1 ]!=a[i]) swap (a[a[i]-1 ],a[i]); for (int i = 0 ;i<n;i++) if (a[i]!=i+1 )return i+1 ; }
C++
2020 线性表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const int maxint = 0x3f3f3f3f bool ismin (int a,int b,int c){ return a<=b&&a<=c; }int distance (int a[],int n1,int b[],int n2,int c[],int n3) { int i = 0 ,j=0 ,k = 0 ,dmin = maxint; while (i<n1 && j<n2&& k <n3 &&d>0 ){ int d = abs (a[i]-b[j])+abs (a[i]-c[k])+abs (b[j]-c[k]); if (d<dmin) dmin=d; if (ismin (a[i],b[j],c[k]))i++; else if (b[j],a[i],c[k]) j++; else k++; } return dmin; }
C++
2009 单链表
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 #include <iostream> using namespace std;struct lnode { int data; lnode *next; };using linklist = lnode*;int getlength (lnode* head) { int length=0 ; lnode *p=head->next; while (p){ length++; p=p->next; } return length; }int findnum (lnode *head,int k) { int length=getlength (head); int n=length-k+1 ; lnode *p=head->next; for (int i = 1 ;i<n;i++){ p=p->next; if (!p)return 0 ; } cout<<p->data<<endl; return 1 ; }
C++
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 #include <iostream> using namespace std;struct lnode { int data; lnode *next; };int findnum (lnode *list,int k) { lnode *p=list->next; lnode *q=list->next; int count = k; while (count--){ if (p==nullptr )return 0 ; p=p->next; } while (p){ p=p->next; q=q->next; } if (q==nullptr ) return 0 ; cout<<q->data<<endl; return 1 ; }
C++
2012 单链表
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 #include <iostream> using namespace std;struct lnode { char data; lnode *next; bool ispass=0 ; };using linklist=lnode *;linklist findpoint (lnode *str1,lnode *str2) { while (str1){ str1->ispass=1 ; str1=str1->next; } while (str2){ if (!(str2->ispass)){ str2->ispass=1 ; str2=str2->next; } else break ; } lnode *p=str2; return p; }
C++
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 #include <iostream> using namespace std;struct node { char data; node *next; };int listlen (node *head) { int len=0 ; while (head->next!=nullptr ){ len++; head=head->next; } return len; }node * findlist (node *str1,node *str2) { int m,n; node *p,*q; m=listlen (str1); n=listlen (str2); for (p=str1;m>n;m--)p=p->next; for (q=str2;m<n;n--)q=q->next; while (p->next!=nullptr &&p->next!=q->next) { p=p->next; q=q->next; } return p->next; }
C++
顺序遍历两个链表到尾结点时,并不能保证两个链表同时到达尾结点。这是因为两个链表的长度不同。假设一个链表比另一个链表长k个结点,我们先在长链表上遍历k个结点,之后同步遍历两个链表,这样就能够保证它们同时到达最后一个结点。因为两个链表从第一个公共结点到链表的尾结点都是重合的,所以它们肯定同时到达第一个公共结点。
1)算法的基本设计思想:
①分别求出str1和str2所指的两个链表的长度m和n。
②将两个链表以表尾对齐:令指针p、q分别指向str1和str2的头结点,若m≥n,则指针p先走,使p指向链表中的第m-n+1个结点;若m<n,则使q指向链表中的第n-m+1个结点,即使指针p和q所指的结点到表尾的长度相等。
③反复将指针p和q同步向后移动,并判断它们是否指向同一结点。当p、q指向同一结点,则该点即所求的共同后缀的起始位置。
2015 单链表
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 #include <iostream> #include <cmath> #include <cstring> using namespace std;struct node { int data; node*link; };void delnode (node *p) { node *q=p->link; p->link=q->link; free (q); }void nosame (node *head,int m,int n) { bool ispass[n+1 ]; memset (ispass,0 ,sizeof (ispass)); node *p=head->link; while (p) { if (!(ispass[abs (p->data)])){ ispass[abs (p->data)]=1 ; p=p->link; head=head->link; } else { delnode (head); p=head->link; } } }
C++
2019 单链表 快慢指针找链表中点
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 #include <iostream> #include <cmath> #include <cstring> using namespace std;struct node { int data; node *next; };int getlength (node *head) { node *p=head->next; int length=0 ; while (p){ length++; p=p->next; } return length; }void insertlist (node *head) { int n=getlength (head); int k = n-(n-1 >>1 ); node *h=head->next; for (int i = 1 ;i<k;i++){ h=h->next; } int m = n-1 >>1 ; node *p=h->next; node *tmp=p->next; p->next=nullptr ; p=tmp; for (int i = 1 ;i<=m;i++){ node *tmp=p->next; p->next=h->next; h->next=p; p=tmp; } node *p=h->next; node *q=head->next; int count= n; node *rear=head; while (count) { node *tmp=q->next; rear->next=q; rear=q; q=tmp; count--; node *tmp=p->next; rear->next=p; rear=p; p=tmp; count--; } rear->next=nullptr ; }
C++
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 #include <iostream> #include <cmath> #include <cstring> using namespace std;struct node { int data; node *next; };void changelist (node *h) { node *p,*q,*r,*s; p=q=h; while (q->next!=nullptr ) { p=p->next; q=q->next; if (q->next!=nullptr ) q=q->next; } q=p->next; p->next=nullptr ; while (q!=nullptr ) { r=q->next; q->next=p->next; p->next=q; q=r; } s=h->next; q=p->next; p->next=nullptr ; while (q!=nullptr ) { r=q->next; q->next=s->next; s->next=q; s=q->next; q=r; } }
C++
你问:“为什么快指针 q 走两步,慢指针 p 走一步,最后 q 到达链尾时 p 正好在链表中间?”
这个其实是快慢指针(龟兔赛跑)找链表中点的经典原理 ,我给你详细解释一下:
原理说明
设链表长度为 n。
慢指针 p 每次走一步,快指针 q每次走两步。
当快指针 q 到达链表末尾(或越过末尾)时,慢指针 p 所指节点就是链表的中点。
为什么成立?
直观示例
链表长度
q走的步数
p 走的步数
p 位置
5
4~5
2~3
第3个节点(中间)
6
6
3
第4个节点(中间)
代码示例
1 2 3 4 5 6 7 8 9 node* findMiddle (node* head) { node* p = head; node* q = head; while (q != nullptr && q->next != nullptr ) { p = p->next; q = q->next->next; } return p; }
CPP
总结
快指针两步,慢指针一步。
快指针到末尾时,慢指针刚好走一半,指向中点。
这是链表中点查找的最常用技巧。
形象文字图
1 2 3 4 5 6 7 less 复制编辑节点: 1 → 2 → 3 → 4 → 5 → 6 → 7 → nullptrp : ↑ ↑ ↑ ↑ 1 2 3 4 q : ↑ ↑ ↑ 1 3 5 7
APACHE
每次循环,p向右走一个节点,q向右走两个节点。
当q到达链尾或超出链尾时,p所在的位置就是中点。
2022 二叉搜索树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> using namespace std;const int max_size =10000 ;struct sqbitree { int sqbitnode[max_size]; int Elenum; };bool isbst (sqbitree a) { int n = a.Elenum; for (int i = 0 ;i<n;i++){ int x=a.sqbitnode[i]; int lk=a.sqbitnode[i*2 +1 ]; int rk=a.sqbitnode[i*2 +2 ]; if (x==-1 )continue ; if (x>=lk &&x<=rk)continue ; else return false ; } return true ; }
C++
2023 邻接矩阵
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 #include <iostream> #include <cstring> using namespace std;const int MAXV=1000 ;struct MGraph { int numVertices,numEdges; char VerticesList[MAXV]; int Edge[MAXV][MAXV]; };int printVertices (MGraph G) { int n = G.numVertices; int m = G.numEdges; int innum[n]; int outnum[n]; memset (innum,0 ,sizeof (innum)); memset (outnum,0 ,sizeof (outnum)); for (int i = 0 ;i<n;i++){ for (int j = 0 ;j<n;j++) { if (m<=0 ) break ; if (G.Edge[i][j]) { innum[j]++; outnum[i]++; m--; } } } int count=0 ; for (int i= 0 ;i<n;i++){ if (outnum[i]>innum[i]){ cout<<char (i+97 ); count++; } } return count; }
C++
2021 邻接矩阵
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 #include <iostream> #include <cstring> #define INF 0x3f3f3f3f using namespace std;const int MAXV=1000 ;struct MGraph { int numVertices,numEdges; char VerticesList[MAXV]; int Edge[MAXV][MAXV]; };int IsExistEL (MGraph G) { int n = G.numVertices,m=G.numEdges; int dexnum[n]; memset (dexnum,0 ,sizeof dexnum); for (int i = 0 ;i<n;i++){ for (int j = 0 ;j<i+1 ;j++){ if (m == 0 ) break ; if (G.Edge[i][j] && G.Edge[i][j]!=INF) { dexnum[i]++; dexnum[j]++; m--; } } } int count = 0 ; for (int i = 0 ;i<n;i++){ if (count >2 ) return 0 ; if (dexnum[i]%2 ) count++; } if (count ==0 || count == 2 )return 1 ; else return 0 ; }
C++
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 #include <iostream> #include <cstring> #define INF 0x3f3f3f3f using namespace std;const int MAXV=1000 ;struct MGraph { int numVertices,numEdges; char VerticesList[MAXV]; int Edge[MAXV][MAXV]; };int IsExistEL (MGraph G) { int degree; int count = 0 ; for (int i = 0 ;i<G.numVertices;i++){ degree =0 ; for (int j = 0 ;j<G.numVertices;j++) degree+=G.Edge[i][j]; if (degree%2 ) count ++; } if (count==0 ||count==2 )return 1 ; else return 0 ; }
C++
2024 拓扑序列
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 时间复杂度O (n²)空间复杂度O (1 )#include <iostream> #include <cstring> using namespace std;const int MAXV=1000 ;struct MGraph { int numVertices,numEdges; char VerticesList [MAXV]; int Edge[MAXV][MAXV]; };int uniquely (MGraph G) { int n =G.numVertices; int d[n]; memset (d,0 ,sizeof d); for (int j = 0 ;j<n;j++) for (int i = 0 ;i<n;i++) d[j]+=G.Edge[i][j]; int hh=-1 ; for (int k = 0 ;k<n;k++){ int dot ; for (int i = 0 ;i<n;i++){ if (!d[i]) { ++hh; dot = i; d[i]=-1 ; } } if (hh!=k) return 0 ; for (int j = 0 ;j<n;j++) if (G.Edge[dot][j]) d[j]--; } if (hh==(n-1 )) return 1 ; else return 0 ; }
C++
2016 排序
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 #include <iostream> using namespace std;void quick_sort (int a[],int l,int r) { if (l>=r)return ; int i = l-1 ,j=r+1 ; int x = a[l+r>>1 ]; while (i<j) { do i++;while (a[i]<x); do j--;while (a[j]>x); if (i<j)swap (a[i],a[j]); } quick_sort (a,l,j),quick_sort (a,j+1 ,r); }void grouping (int n,int a[]) { quick_sort (a,0 ,n-1 ); int n1=n>>1 ; int n2=n-n1; int a1[n1]; int a2[n2]; for (int i = 0 ;i<n1;i++) a1[i]=a[i]; for (int i = n1,j=0 ;i<n;i++,j++)a2[j]=a[i]; }
C++
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 #include <iostream> using namespace std;int setPartition (int a[],int n) { int pivot,low = 0 ,low1=0 ,high=n-1 ,high1=n-1 ,flag=1 ,k=n>>1 ,i; int s1=0 ,s2=0 ; while (flag) { pivot=a[low]; while (low<high){ while (low<high && a[high]>=pivot) --high; if (low!=high) a[low]=a[high]; while (low<high && a[low]<=pivot) ++low; if (low!=high) a[high]=a[low]; } a[low]=pivot; if (low == k-1 ) flag =0 ; else { if (low <k-1 ) { low1=++low; high=high1; } else { low = low1; high1=--high; } } } for (int i = 0 ;i<k;i++)s1+=a[i]; for (int i = k;i<n;i++)s2+=a[i]; return s2-s1; }
C++