数据结构与算法程序设计总结
by ldc
难度:北京大学数学科学学院数据结构与算法(B)
教材:《算法与数据结构——C语言描述》(第三版) 张乃孝,陈光,孙猛 编著
Chapter2 线性表
顺序表,单链表,双链表(可以向前也可以向后),循环链表(最后一个结点指向头结点),循环双链表
作业1:顺序表 错题: 多项式加法(不知道发生了啥事,看来还是越简单(sort)越容易对) 数组元素循环右移(这个nb的,要记得mod m)
注意:链表要带头才比较好用,否则删除第一个节点需要特殊的操作 单链表:
struct node{
node * link;
Datatype info;
};
typedef node * pnode;
pnode createNullList(){
pnode pa=new(node);
pa->link=NULL;
return pa;
}
int isNull(pnode pa){
if(pa->link==NULL){
return 1;
}
else{
return 0;
}
}
void insert_back(pnode pa, Datatype tempinfo){
pnode tempp=pa;
while(tempp->link!=NULL){
tempp=tempp->link;
}
tempp->link=createNullList();
tempp->info=tempinfo;
}
int delete_back(pnode pa){
pnode tempp=pa;
if(isNull(pa)==1){
return 2;//empty
}
while(tempp->link->link!=NULL){
tempp=tempp->link;
}
delete(tempp->link->link);
tempp->link=NULL;
return 1;//删除成功
}作业3:注意一下斜杠的位置!‘\n’
Chapter3 字符串
核心算法:KMP(模式匹配算法) string和char的比较:string几乎没有什么缺点,但是有的时候做字符串截取的时候比较容易出错,一定要记牢字符串截取时的那些参数的含义 实现KMP:
#include <iostream>
using namespace std;
//KMP
struct str{
int len;
char word[1000];
};
str templ,target;
str input_(){
str tempstr;
tempstr.len=0;
char temp;
while((temp=cin.get())!=EOF){
if(temp=='\n'){
break;
}
if(temp!=' '){
if(temp>='A'&&temp<='Z'){
temp=temp-'A'+'a';
}
tempstr.word[tempstr.len]=temp;
tempstr.len++;
}
}
return tempstr;
}
int next_[1000]={0};
void calcuNext(){
int i,j,k;
next_[0]=-1;
k=-1;
i=0;
while(i<templ.len-1){
while(k>=0&&templ.word[i]!=templ.word[k]){
k=next_[k];
}
i++;
k++;
next_[i]=k;
}
}
int cmp(){
int flag1=0;
int flag2=0;
while(flag1<templ.len&&flag2<target.len){
if(flag1==-1||templ.word[flag1]==target.word[flag2]){
flag1++;
flag2++;
}
else{
flag1=next_[flag1];
}
}
if(flag1==templ.len){
return 1;
}
return 0;
}
int main()
{
templ=input_();
target=input_();
if(templ.len>target.len){
str tempstr;
tempstr=templ;
templ=target;
target=tempstr;
}
calcuNext();
if(cmp()==1){
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
练习4:注意大整数加法里面两个加数都为0的情况
Chapter 4 栈与队列
栈:先进先出 stack 应用:中缀转后缀,表达式计算 表达式计算的中间矩阵真的是太太太麻烦了,我赌他不会考,考出来就是我输
队列:后进后出 queue 回溯:经典的八皇后问题
#include<iostream>
#include<string.h>
#include<stack>
#include<math.h>
using namespace std;
int a[100][8];//记录所有八皇后串的顺序
int flag=1;//从1开始
//int used[9]={0};
int templ[9]={0};//记录临时局面
int crash(int row,int pos){
for(int j=1;j<=row-1;j++){
if(templ[j]==pos){
return 0;
}
if(abs(row-j)==abs(pos-templ[j])){
return 0;
}
}
return 1;
}
int calcu(int row){//现在正在摆放第row行的皇后
if(row==9){
for(int j=1;j<=8;j++){
a[flag][j]=templ[j];
}
flag++;
return 0;
}
for(int i=1;i<=8;i++){
if(crash(row,i)==1){
templ[row]=i;
calcu(row+1);
}
}
return 0;
}
int main(){
int n;
cin>>n;
calcu(1);
int i,j;
for(j=1;j<=n;j++){
int temp;
cin>>temp;
for(int i=1;i<=8;i++){
cout<<a[temp][i];
}
cout<<endl;
}
}
一遍AC,这样好
Chapter 5 二叉树和树
这一章比较重要的是概念:
节点的层数:一般规定根节点的层数为0,其余节点的层数为父节点的层数+1
二叉树的高度:二叉树中节点的最大层数
节点的度数:节点的非空子树的个数
路径长度:x是y的祖先,那么x=x0,x1,x2,...,xn=y称为x到y的路径,并且n称为这条路径的长度
满二叉树:每个节点要么是树叶,要么有两个子树(即:节点总数必然为2^n-1)
完全二叉树:相当于以顺序表存储时的结构:除了最后一层以外都是满的,最后一层的节点也全都在左边
扩充二叉树:每个没有满的节点都使用分支节点填满,新增的节点称为外部节点,原有的节点称为内部节点
二叉树的周游:
先序遍历,中序遍历,后序遍历(算法:二推一/还原树:查找式递归)
在已知扩充二叉树的X序遍历后还原树(算法:直接递归还原)
typedef int Datatype;
struct binTree{
binTree* left;
binTree *right;
Datatype info;
};
typedef binTree * pbinTree;
pbinTree createNull(){
pbinTree pa=new(binTree);
pa->left=NULL;
pa->right = NULL;
return pa;
}
int isNull(pbinTree pa){
if(pa->left==NULL&&pa->right==NULL){
return 1;
}
return 0;
}
二叉树的应用:
1.堆与优先队列:
优先队列虽然名字叫做队列,但事实上和队列完全没有关系,更不具备先进先出的特性。优先队列的本质是一个动态维护的有序堆,并且每次取出的是堆的根节点。因此,如果构造一个小根堆(根节点最小)的话,每次取出的就是最小元素,反之亦然。
2.哈夫曼树:
基本思想:把所有东西都存在一个顺序表里面,逐步贪心,仔细想想就好了,和上次考试的第二题是一样的。
树:
树的表示:固定使用子表表示法好了,这个咱熟
树和二叉树的转换:
树->二叉树:
操作:使用递归即可:
单步:将一个树林转换成二叉树:让每个弟节点的父亲变成兄节点即可
(最适合的数据类型:子表表示法)
二叉树->树:
操作:把所有连成一条线的右子节点列都变成第一个节点的父节点的儿子
(最适合的数据类型:父节点表示法或者子表表示法)
Chapter 6 集合与字典
字典的高级检索:二分法检索(字典的关键码要进行有序排列)
散列表:非常难用,推荐使用map,见附录,map简直秒杀散列表的好吧
练习7应用:
并查集,带权并查集(核心)
在路径压缩的过程中可以使用递归,到时候仔细算一下就行了
#include <stdio.h>
#include<iostream>
#include<math.h>
#include <string.h>
#define maxint 100001
using namespace std;
struct ani{
int parent;
int weight; //0:和parent是同类,1:吃parent,2:被parent吃
};
ani animal[maxint];
int n;//总animal数
void initialzer(){
for(int i=1;i<=n;i++){
animal[i].parent = i;
animal[i].weight = 0;
}
}
int findd(int x){
if(animal[x].parent==x){
return x;
}
int t=animal[x].parent;
animal[x].parent=findd(animal[x].parent);
animal[x].weight = (animal[x].weight + animal[t].weight) % 3;
return animal[x].parent;
}
int union_(int x,int y,int s){//x吃y输入1,否则0
if(x==y&&s==1){
return 0;//假话
}
if(x>n||y>n){
return 0;//假话
}
int rx = findd(x);
int ry = findd(y);
int wx = animal[x].weight;
int wy = animal[y].weight;
if(rx==ry&&(wx+3-s-wy)%3!=0){
return 0;//假话
}
animal[ry].weight = (wx - wy+3-s) % 3;
animal[ry].parent = rx;
return 1;//真话
}
int main(){
cin >> n;
int k;
cin>>k;
int x, y, ea,judge;
int cnt = 0;
initialzer();
while(k--){
cin>>ea>>x>>y;
judge = union_(x, y, ea-1);
if(judge==0){
cnt++;
//cout << "fake news!" << endl;
}
else{
//cout << "real news!" << endl;
}
}
cout << cnt << endl;
}
Chapter 7 高级索引
数据结构:字符树(垃圾),二叉排序树(右子树里面所有节点的值都比根小,左子树里面所有节点的值都比根大)算法非常复杂,要调来调去,但是写起来就是一个map的事情。
二叉排序树和堆是有区别的,堆的话是根节点比子树的所有节点都要大/小,而排序树则是右<根<左。应用也不一样,堆是用来保障每次取出最小元素,排序树是用来保证每次插入速度最快。(相当于每次都用二分法插入)
二叉排序树有两个常见的优化:最佳二叉排序树和平衡二叉排序树
练习8:常见问题:
(1)注意一下,数字的大小顺序和看成字符串以后的字典顺序是不一样的,比如在字典序中1>010
(2)拼写检查的重点在于把所有的字符串都存储下来之后还要进行剪枝(长度差大于1的直接忽略不计)
(3)仔细看一下题目里面有没有说输入的字符串可以带空格!!!!!
Chapter 8 排序
(1)插入排序:维护一个顺序表,每次向其中插入数据。
常见算法:二分法插入排序(每次找一半就行了)稳定
(2)选择排序:每次都从待排序记录中选出排序码最小的记录
常见算法:堆排序:先把数据构造成一个大根堆,然后直接依次输出根节点
核心算法:构造初始堆:首先把所有数据都放在顺序表里面,然后从第一个有子节点的数据开始向前,把以每个数据为根的树调整为堆。不稳定
(3)交换排序:不停地把逆序对换成正序的
常见算法:冒泡排序:对(R0,R1),(R1,R2),...,(Rn-1,Rn)进行换序,那么最后Rn一定会放着最大的,然后对前n-1个继续……为了提升速度,可以设置一个flag来看某一次是否做了交换,如果没有交换就说明排序已经结束。
快速排序:核心思想:选取一个数据,把所有小于之的都放到左面,大于之的都放到右面。不稳定
(4)分配排序:把排序码分成若干个部分:
常见算法:基数排序法:先把数据按照个位分开,把十位排好,再按照个位从小到大合起来,再按照十位分开,把个位排好,再合在一起就行了。稳定
(5)归并排序:相当nb,求了一波逆序对:两组+两路+一路
附录:集中复习一下各种库的用法:
1.#include<string.h>
string a==string b; string a.length(); string a.append(string b);
string a.substr(int n,int m);//n:子串的起始位置(从0开始计),m:求出的子串的长度
(string a.find(string b)=n)!=string::npos 返回b在a中出现的第一个位置,如果没有就会返回一个静态常量string::npos
2.#include<stack>/queue
stack <int> a; stack a.pop(); stack a.top(); stack a.push(); stack a.empty();
3.优先队列
操作和普通队列相同:top,empty,pop,push,size
普通的定义方式: priority_queue<int, vector<int>, greater<int>>pa;
priority_queue<int, vector<int>, less<int>>pb;
更加一般的定义方式:
struct tmp{
int info;
};
struct tmp1{
bool operator()(tmp a,tmp b){
return a.info < b.info;//这个方向定义的就是大根堆,每次弹出的是最大元素
}
};
priority_queue<tmp,vector<tmp>,tmp1> h;
4.map
map是STL的一个关联容器,它提供一对一的hash。
第一个可以称为关键字(key),每个关键字只能在map中出现一次; 第二个可能称为该关键字的值(value);
map以模板(泛型)方式实现,可以存储任意类型的数据,包括使用者自定义的数据类型。Map主要用于资料一对一映射(one-to-one)的情況,map內部的实现自建一颗红黑树,这颗树具有对数据自动排序的功能。在map内部所有的数据都是有序的。
#include<map> #include<string>//如果不想让红黑树爆炸的话
map<int,string> pa;//以int作为索引,指向string的指针
(1)pa.insert(pair<int,string> (000,"student_zero"));//使用insert函数插入键值对!
(2)pa[000]=student_zero;//直接使用array方式插入zero
iter=pa.find(key1);//查找键为key1的数据(注:iter的数据类型建议使用auto)
if(iter!=pa.end());//如果返回map::end位置,就说明没有找到,否则iter的值即为所求
pa.size();返回map的大小
pa.erase(key1):删除key1和对应的值
另外,unordered_map对应着哈希表,用它来实现的话内部没有红黑树,但是如果只是查找的话效率为O(1)
本文深入探讨数据结构与算法的基础知识,涵盖线性表、字符串处理、栈与队列等核心概念,详解二叉树及树的表示方法,同时涉及集合与字典的高效实现策略,以及多种排序算法的原理与应用场景。
7980

被折叠的 条评论
为什么被折叠?



