数据结构与算法上机考试复习笔记

本文深入探讨数据结构与算法的基础知识,涵盖线性表、字符串处理、栈与队列等核心概念,详解二叉树及树的表示方法,同时涉及集合与字典的高效实现策略,以及多种排序算法的原理与应用场景。

数据结构与算法程序设计总结

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)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值