合工大胡学钢版 6.30号开始数据结构 7.18第一轮结束 7.19开始肝题,后续会不定期补充一些做题的问题进来
习题在P13 这一章主要理清概念和术语,不要混淆
1.1数据 数据元素 数据结构
1.1.1数据是信息的载体,是指能输入到计算机中,并被计算机识别、存储和处理的符号的集合。
1.1.2数据元素是数据中具有独立意义的个体,有时也叫元素,记录,结点,顶点
1.1.3字段(域)是对元素的详细描述,即元素可能包含多个字段。
1.1.4数据结构是组成数据的元素之间的结构关系。
1.2数据结构三要素
1.2.1 数据结构三要素分别是逻辑结构、存储结构和运算
1.2.2 三者的关系
1.2.3算法是对特定问题求解步骤的一种描述。算法具有5个重要特征:有穷性,确定性,可行性,输入,输出;一个好的算法还具有:正确性,可读性,健壮性,效率与低存储需求。
1.3复杂度
1.3.1 对算法能进行时间复杂度和空间复杂度的分析
1.4错题
王道 p3 1.1.3试题精选
1.抽象数据类型描述了数据的逻辑结构和抽象运算,通常用(数据对象,数据关系,基本操作集)这样的三元组来表示,形成了完整的数据结构定义
2.数据的逻辑结构只采用抽象方式表达,独立于存储结构;存储结构是逻辑结构在计算机上的映射,它不能独立于逻辑结构存在。
3.链式存储结构中的结点的存储单元地址是连续的(注意是,是结点内!!)
2.1栈
2.1.1 先进后出的特点,和栈类的一些常用方法
2.1.2 一种特殊的运算受限的线性表
2.1.3 若入栈下标为1~n,则合法的栈的输出序列满足:每一个入栈下标它后面没有比它小的数字而且是按递减排列的
2.1.4 Cn2n/n+1
2.2顺序栈的C++实现
//栈
class m_stack {
public:
//顺序表长
enum {MAX_SIZE=100,};
//状态
enum class status{EMPTY_STACK = 0,FULL_STACK,SUCESS};
//构造函数
m_stack() {
count = 0;
}
//入栈
status push(int var);
//出栈
status pop();
//取栈顶
status top(int &var);
//空
bool empty() { return count == 0; }
private:
//顺序表
int arr[MAX_SIZE];
//计数值
int count;
};
//入栈
m_stack::status m_stack::push(int var)
{
if (count == MAX_SIZE) return status::FULL_STACK;
arr[count++] = var;
return status::SUCESS;
}
//弹出
m_stack::status m_stack::pop()
{
if (count == 0) return status::EMPTY_STACK;
count--;
return status::SUCESS;
}
//取栈顶
m_stack::status m_stack::top(int &var)
{
if (count == 0) return status::EMPTY_STACK;
var= arr[count - 1];
return status::SUCESS;
}
2.3栈的应用示例
2.3.1 课本例2.1反序输入
读入n个整数,按照相反次序输出
void reverse_out(int *A,int length)
{
stack<int > s;
for(int i=0;i<length;i++)
s.push(A[i]);
while(!s.empty()){
cout<<s.top()<<endl;
s.pop();
}
}
2.3.2 例2.2 十进制转八进制
void convert_out(int var)
{
stack<int > s;
while(var!=0)
{
int remains=var%8;
var/=8;
s.push(remains);
}
while(!s.empty()){
cout<<s.top();
s.pop();
}
cout<<endl;
}
int main()
{
convert_out(256894);
return 0;
}
2.3.2 习题2.1 判断是否是栈的合法输出序列
这里使用模拟
P4387 【深基15.习9】验证栈序列
bool check(int *a,int *b,int length)
{
stack<int > s;
int count=0;
for(int i=0;i<length;i++)
{
s.push(a[i]);
while(!s.empty()&&s.top()==b[count]){
count++;
s.pop();
}
}
if(s.empty()) return true;
else return false;
}
2.3错题
王道 p63 3.1.4试题精选 选择题1-26 错误数:3
1. 栈和队列的逻辑结构是相同的,都是线性结构(我用排除法,去掉了三个选项)
2. 20题看错了,题目给的是输出序列,不是输入序列,淦
3. 22题数数都数歪来
4. 采用共享栈的好处是节省存储空间,降低发生上溢的可能
3.1 基本概念
3.1.1 队列FIFO特性
3.2 循环队列的实现
洛谷 P1443马的遍历
手写完成循环队列后,从别人的解答里copy了一份程序用自己的队列替换了别人程序中的STD queue进行测试
template <class T> class queue
{
public:
enum{MAX_SIZE=50000,};
queue() {
top=rear=psize=0;
}
bool push(T var);
bool pop();
T front();
bool empty();
bool full();
int size(){
return psize;
}
private:
T arr[MAX_SIZE];
int top;
int rear;
int psize;
};
template <class T> bool queue<T>::pop()
{
if(empty()) return false;
else
{
top=(top+1)%MAX_SIZE;
psize--;
return true;
}
}
template <class T> T queue<T>::front()
{
if(empty()) {
throw out_of_range("error:empty queue");
}
else
{
return arr[top];
}
}
template <class T> bool queue<T>::push(T var)
{
if(full()) return false;
else{
arr[rear]=var;
rear=(rear+1)%MAX_SIZE;
psize++;
}
}
template <class T> bool queue<T>::empty()
{
return (size()==0);
}
template <class T> bool queue<T>::full()
{
return (size()==MAX_SIZE-1);
}
3.2.1 队列应用 课本例3.1 打印杨辉三角
#include
#include
using namespace std;
int main()
{
queue<int> q;
int temp;int pre;
int all=8;
for(int i=1;i<=all;i++)
{
pre=0;
for(int j=1;j<=i;j++)
{
if(i==j)
{
q.push(1);
cout<<1<<endl;
}
else
{
temp=q.front();
cout<<temp+pre<<" ";
q.push(temp+pre);
pre=temp;
q.pop();
}
}
cout<<endl;
}
return 0;
}
输出的结果
[Running] cd "c:\Users\RECPET-D\Desktop\dsLearning\" && g++ example3_1.cpp -o example3_1 && "c:\Users\RECPET-D\Desktop\dsLearning\"example3_1
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
[Done] exited with code=0 in 1.306 seconds
4.1 链栈
手撸一个链栈 这里书上有点问题,一会带头结点,一会不带
第一种:不带头结点的链栈
#include
#include
using namespace std;
template <class T> struct node{
T data;
node* next;
};
template <class T> class stack{
public:
stack();
~stack();
bool empty();
bool push(T var);
bool pop();
T top();
private:
node<T> *cur;
int count;
};
template<class T> stack<T>::stack()
{
count=0;
cur=NULL;
}
template<class T> stack<T>::~stack()
{
while (!empty())
{
pop();
}
std::cout<<"destructor"<<endl;
}
template <class T> bool stack<T>::empty()
{
return count==0;
}
template <class T> bool stack<T>::push(T var)
{
node<T>* temp=new node<T>;
temp->data=var;
temp->next=cur;
cur=temp;
count++;
return true;
}
template <class T> bool stack<T>::pop()
{
if(empty()) return false;
count--;
node<T> *temp=cur;
cur=cur->next;
delete temp;
return true;
}
template <class T> T stack<T>::top()
{
if(empty()) {
throw out_of_range("error:empty stack!");
}
return cur->data;
}
//简单测试
int main()
{
stack<int > s;
for(int i=0;i<100;i++)
{
s.push(i);
}
while(!s.empty())
{
cout<<s.top()<<endl;
s.pop();
}
return 0;
}
第二种:带头结点的链栈,区别和上面主要是在push,top,和pop三个方法的实现
构造函数里要给头结点分配内存
template<class T> stack<T>::stack()
{
count=0;
cur=new node<T>;
cur->next=NULL;
}
析构函数里需要注意释放完后,头结点还没被释放
template<class T> stack<T>::~stack()
{
while (!empty())
{
pop();
}
//释放头结点
delete cur;
}
入栈操作
template <class T> bool stack<T>::push(T var)
{
node<T>* temp=new node<T>;
temp->data=var;
temp->next=cur->next;
cur->next=temp;
count++;
return true;
}
弹栈操作
template <class T> bool stack<T>::pop()
{
if(empty()) return false;
count--;
node<T> *temp=cur->next;
cur->next=temp->next;
delete temp;
return true;
}
取栈顶操作
template <class T> T stack<T>::top()
{
if(empty()) {
throw out_of_range("error:empty stack!");
}
return cur->next->data;
}
4.2 链队列
手撸一个链队列
template <class T> struct node{
T data;
node* next;
};
template <class T> class queue{
public:
queue();
~queue();
bool push(T var);
bool pop();
T front();
bool empty();
private:
int count;
node<T> *cur,*rear;
};
template <class T> queue<T>::queue()
{
count=0;
cur=new node<T>;
rear=cur;
cur->next=NULL;
}
template <class T> queue<T>::~queue()
{
while(!empty()){
pop();
}
delete cur;
}
template <class T> bool queue<T>::push(T var)
{
node<T> * temp= new node<T>;
temp->data=var;
temp->next=NULL;
rear->next=temp;
rear=temp;
count++;
return true;
}
template <class T> bool queue<T>::empty()
{
return count==0;
}
template <class T> bool queue<T>::pop()
{
if(empty()) return false;
node <T> *temp =cur->next;
cur->next=temp->next;
count--;
delete temp;
if(cur->next==NULL) rear=cur;
return true;
}
template <class T> T queue<T>::front()
{
if(empty()) {
throw out_of_range("error:queue empty!");
}
return cur->next->data;
}
具体测试,还是上次从洛谷的题解洛谷 P1443马的遍历中随机复制了一份题解,去掉“queue”头文件,在前面加入我手撸的queue,点击提交
最后通过了,说明应该没啥问题
4.3 总结
4.3.1熟悉指针操作即可,重点在后面
4.3错题
王道 p79 3.2.5试题精选 选择题1-20 错误数:5
1.注意给出数组的范围
2. 一般入队只改动rear,出队只改动front
3. 做题仔细一点
b站白嫖结束,准备买课,(●’◡’●)重点章节由此开始冲呀!!
5.1线性表的定义和运算
5.1.1线性表是由n个元素构成的有限序列
5.1.2直接前驱和直接后继的概念
5.1.3基本运算
init();length();get_element();locate();insert();delete_element();
5.2顺序表
直接上实现,课本里获取元素是实现了一个get_element方法,我这里直接对操作符[]进行重载
template <class T> class list
{
public:
enum{max_size=1000,};
list();
int length();
T& operator[] (unsigned int i);
int locate(T var);
bool insert(int i,T var);
bool discard(int i);
bool push_back(T var);
private:
T arr[max_size];
int lens;
};
template <class T> list<T>::list()
{
lens=0;
}
template <class T> int list<T>::length()
{
return lens;
}
template <class T> T& list<T>::operator[](unsigned int i)
{
if(i<lens)
return arr[i];
else
throw out_of_range("out of list range!");
}
template <class T> int list<T>::locate(T var)
{
for(int i=0;i<lens;i++){
if(var==arr[i]){
return i;
}
}
return -1;
}
template <class T> bool list<T>::insert(int i,T var)
{
if(lens==max_size) return false;
if(i<1||i>length()+1) return false;
for(int j=length()-1;j>=i-1;j--){
arr[j+1]=arr[j];
}
arr[i-1]=var;
lens++;
return true;
}
template <class T> bool list<T>::discard(int i)
{
if(lens==max_size) return false;
if(i<1||i>length()+1) return false;
for(int j=i;j<lens-1;j++){
arr[j-1]=arr[j];
}
lens--;
return true;
}
template <class T> bool list<T>::push_back(T var)
{
if(lens==max_size) return false;
arr[lens++]=var;
return true;
}
5.3链表
5.3.1链表结构和运算的实现
先撸了再说
这里书上的下标给我整混乱了,我直接按照数组下标从0开始写的代码,同样课支持按[]访问
#include
#include
using namespace std;
template <class T> struct node{
T data;
node* next;
};
template <class T> class list{
public:
list();
~list();
int length();
T& operator[](unsigned int i);
node<T>* locate(T var);
bool insert(int i,T var);
bool discard(int i);
node<T>* head(){return Head;};
private:
int lens;
node<T> *Head;
};
template <class T> list<T>::list()
{
lens=0;
Head=new node<T>;
Head->next=NULL;
}
template <class T> list<T>::~list()
{
while(lens!=0){
//cout<<"destructor lens="<
discard(0);
}
delete Head;
}
template <class T> int list<T>::length()
{
//o(1)复杂度
return lens;
//o(n)复杂度
// node *p = Head->next;
// int ret = 0;
// while (p != NULL)
// {
// p = p->next;
// ret++;
// }
// return ret;
}
template <class T> T& list<T>::operator[](unsigned int i)
{
node<T> *p=Head->next;
int tar=0;
while(p!=NULL&&tar!=i){
p=p->next;
tar++;
}
if(p==NULL) throw out_of_range("out of list range!");
return p->data;
}
template <class T> node<T>* list<T>::locate(T var)
{
node<T> *p=Head->next;
while(p!=NULL){
if(p->data==var) return p;
p=p->next;
}
return NULL;
}
template <class T> bool list<T>::insert(int i,T var)
{
node<T> *p=Head;
int tar=0;
if(i<0||i>lens) return false;
while(p!=NULL&&tar!=i){
p=p->next;
tar++;
}
node<T>* temp=new node<T>;
temp->data=var;
temp->next=p->next;
p->next=temp;
lens++;
return true;
}
template <class T> bool list<T>::discard(int i)
{
node<T> *p=Head;
int tar=0;
if(i<0||i>=lens) return false;
while(p!=NULL&&tar!=i){
p=p->next;
tar++;
}
node<T>* temp=p->next;
p->next=temp->next;
delete temp;
lens--;
return true;
}
int main()
{
//创建一个链表
list<int> arr;
//插入10个数
for(int i=0;i<10;i++) arr.insert(i,i);
//插入下标8,元素为99
arr.insert(8,-99);
//删除下标为10的元素(0~10,第十一个)
arr.discard(10);
//输出长度
cout<<"长度:"<<arr.length()<<endl;
//按内容找
cout<<"找-99的结点,输出->data:"<<arr.locate(-99)->data<<endl;
//输出
for(int i=0;i<arr.length();i++)
{
cout<<"下标:"<<i<<" 数据:"<<arr[i]<<endl;
}
return 0;
}
输出的结果
[Running] cd "c:\Users\RECPET-D\Desktop\dsLearning\" && g++ linkList.cpp -o linkList && "c:\Users\RECPET-D\Desktop\dsLearning\"linkList
长度:10
找-99的结点,输出->data:-99
下标:0 数据:0
下标:1 数据:1
下标:2 数据:2
下标:3 数据:3
下标:4 数据:4
下标:5 数据:5
下标:6 数据:6
下标:7 数据:7
下标:8 数据:-99
下标:9 数据:8
[Done] exited with code=0 in 1.184 seconds
5.3.2具体应用
买的视频一机一码有点烦人,先换电脑
鹏保宝简直脑tan,体验感极差,还是先自学吧,感觉这波属于智商税了
补充:课本关于链表构造的方法,这里只写了头插
template <class T> list<T>::list(T *arr,int arr_size)//overload2
{
//初始化
lens=0;
Head=new node<T>;
Head->next=NULL;
//头插
int count=0;
while(count<arr_size)
{
node<T> *temp=new node<T>;
temp->data=arr[arr_size-1-count];
temp->next=Head->next;
Head->next=temp;
count++;
lens++;
}
}
例5.5的题,这是一种复杂度比较好的做法
bool isIncrease(list<int> list)
{
node<int> *cur=list.head()->next;
if(cur==NULL) return false;//空表
while(cur->next!=NULL)
{
if(cur->data<=cur->next->data){
cur=cur->next;
}
else return false;
}
return true;
}
当然,由于有操作符重载,还可以这样,虽然看着像数组,不过这样复杂度比较高,因为每次按[]访问实际上都是在从头遍历链表
for(int i=0;i<arr.length()-1;i++)
{
if(arr[i]>arr[i+1]){
cout<<"false"<<endl;
return 0;
}
}
cout<<"true"<<endl;
例5.8 18年考题 汗了,中间漏写了两行直接给我电脑带走了/(ㄒoㄒ)/~~
template <class T> list<T>::list(list<T> &list_a,list<T> &list_b)
{
lens=0;
Head=new node<T>;
Head->next=NULL;
node<T> *pa,*pb,*rear,*temp;
pa=list_a.head()->next;
pb=list_b.head()->next;
rear=Head;
while(pa!=NULL&&pb!=NULL)
{
if(pa->data==pb->data){
temp=new node<T>;
temp->data=pa->data;
rear->next=temp;
rear=temp;
lens++;
pa=pa->next;
pb=pb->next;
}
else if(pa->data>pb->data){
pb=pb->next;
}
else{
pa=pa->next;
}
}
}
5.3.3 其它结构的链表
略,没什么难的
5.4串
非重点,熟悉操作即可
5.6错题
王道 p16 2.2.3试题精选
1.线性表的顺序存储结构是一种随机存取的存储结构(这里存取是指读取方式)
2.线性表元素的序号是从1开始,而在n+1个位置插入相当于表尾追加
王道 p37 2.3.7试题精选
1.23题看错了节点顺序
王道 p99 3.4.5试题精选
1.第5题错,忘了三对角矩阵第一行和最后一行比较特殊了,淦
基本操作,这里关注一下两个不常考的考点
非重要考点一 递归的正确性
①逻辑正确性(能通过数学方法证明正确)和实现正确性(对给定范围的任何数,都产生正确的运行结果)
非重要考点二 递归转换成非递归程序
①先mark住,通用的方法比较麻烦,具体问题具体分析比较简单
了解广义表的操作即可
20分左右,重点!
8.1树
8.1.1 树的定义:树是n个结点构成的有限集,任一非空树应当满足
1.有且仅有一个特定的称为根
2.当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2…Tm,每个集合本身也是树,并且称为根的子树
显然,树还具有以下性质:
1.根结点没有前驱,除根结点以外的所有结点有且只有一个前驱
2.树中所有结点可以有零个或多个后继
3.树的结点数等于所有结点度数之和+1
4.度为m的树中第i层至多有m^i-1个结点
5.高度为h的m叉树至多有(m^h-1)(m-1)个结点
6.具有n个结点的m叉树的最小高度为logm(n(m-1)+1)
8.1.2 m叉树和度为m的树的区别:
度为m的树 | m叉树 |
---|---|
任意结点度<=m | 同左 |
至少有一个结点度为m | 允许所有度 |
一定是非空树,至少m+1个结点 | 可以是空树 |
8.2二叉树
8.2.1 二叉树的性质:
1.在二叉树的第i层上的结点数<=2^i-1
2.深度为k的二叉树的结点数<=2^k-1
3.对一颗非空二叉树,叶子数n0,度为2的结点数为n2,则有n0=n2+1
8.2.2 完全二叉树的性质:
1.有n个结点的非空完全二叉树的深度为[log2n]+1
2.左孩子[2i],右孩子[2i+1],父节点[i/2]
8.3 二叉树代码实现和遍历
基于模板的二叉树实现,由中序和先序输入序列构建
8.3.1 例子1:
写的比较乱/(ㄒoㄒ)/~~,太菜了太菜了
#include
#include
#include
using namespace std;
template <class T>struct node
{
T data;
node*lchild;
node*rchild;
};
enum class TreeOrder{
PRE=1,IN,POST
};
template<class T> class biTree
{
public:
biTree(void (*ex_print)(T var));
node<T>* create(string pre,string in,int prel,int prer,int inl,int inr);
void clear(node<T>* root);
void print(TreeOrder order);
void assign(node<T>* root_node);
void (*elemType_print)(T var);
~biTree();
private:
void preorder_print_dfs(node<T>* root);
void inorder_print_dfs(node<T>* root);
void postorder_print_dfs(node<T>* root);
node<T> *c_root;
};
template<class T> biTree<T>::biTree(void (*ex_print)(T var))
{
//cout<<"construct!"<
c_root=NULL;
elemType_print=ex_print;
}
template <class T> node<T>* biTree<T>::create(string pre,string in,int prel,int prer,int inl,int inr)
{
if(prel>prer) return NULL;
node<T>*root=new node<T>;
root->data=pre[prel];
int k=0;
for(k=inl;k<=inr;k++){
if(pre[prel]==in[k]) break;
}
int numLtree=k-inl;//左子树的结点数
root->lchild=create(pre,in,prel+1,prel+numLtree,inl,k-1);
root->rchild=create(pre,in,prel+numLtree+1,prer,k+1,inr);
return root;
}
template<class T> void biTree<T>::assign(node<T>* root_node)
{
c_root=root_node;
}
template<class T> biTree<T>::~biTree()
{
clear(c_root);
}
template<class T> void biTree<T>::clear(node<T>* root)
{
if(root==NULL){
return;
}
clear(root->lchild);
clear(root->rchild);
delete root;
}
template<class T> void biTree<T>::preorder_print_dfs(node<T>* root)
{
if(root==NULL){
return;
}
elemType_print(root->data);
preorder_print_dfs(root->lchild);
preorder_print_dfs(root->rchild);
}
template<class T> void biTree<T>::inorder_print_dfs(node<T>* root)
{
if(root==NULL){
return;
}
inorder_print_dfs(root->lchild);
elemType_print(root->data);
inorder_print_dfs(root->rchild);
}
template<class T> void biTree<T>::postorder_print_dfs(node<T>* root)
{
if(root==NULL){
return;
}
postorder_print_dfs(root->lchild);
postorder_print_dfs(root->rchild);
elemType_print(root->data);
}
template<class T> void biTree<T>::print(TreeOrder order)
{
if(order==TreeOrder::PRE) preorder_print_dfs(c_root);
else if(order==TreeOrder::IN) inorder_print_dfs(c_root);
else if(order==TreeOrder::POST) postorder_print_dfs(c_root);
}
//自己实现输出方法,实例化时函数作为参数
void char_print(char var){
cout<<var;
}
int main()
{
string pre="ABDECF";
string in="DBEACF";
biTree<char> tree(char_print);
tree.assign(tree.create(pre,in,0,pre.length()-1,0,in.length()-1));
cout<<"先序遍历";tree.print(TreeOrder::PRE);cout<<endl;
cout<<"中序遍历";tree.print(TreeOrder::IN);cout<<endl;
cout<<"后序遍历";tree.print(TreeOrder::POST);cout<<endl;
return 0;
}
输出结果
[Running] cd "c:\Users\RECPET-D\Desktop\dsLearning\" && g++ -std=c++11 btree.cpp -o btree &&"c:\Users\RECPET-D\Desktop\dsLearning\" btree
先序遍历ABDECF
中序遍历DBEACF
后序遍历DEBFCA
[Done] exited with code=0 in 1.284 seconds
其它例子
书上的其它例子都比较简单,就不写了
8.4 线索二叉树
先序后继的求解
threadNode* get_pre_node(threadNode* node)
{
if(node->ltag==0) return node->left;
return node->right;
}
中序后继的求解
threadNode* get_in_node(threadNode* node)
{
threadNode* temp=node->right;
if(node->rtag==1) return node->right;//有线索后继,直接返回
//没有线索后继,找右子树里左下那个结点
while(node->ltag==0){
temp=temp->left;
}
return temp;
}
8.5 树和森林
8.5.1 双亲表示法,孩子链表表示法,孩子兄弟链表表示法,二叉树和森林的转换
8.6 哈夫曼树
8.6.1 带权路径长度最小的树也就是哈夫曼树,又称最优二叉树,对于同一组数,可能存在不同的哈夫曼树,但是树的带权路径长度一定是相等的。
下面给出构建哈夫曼树的一般方法:
①对于给定的n个数,构建n颗树与之对应
②将根结点权值最小的两颗树进行合并,合并后的根结点权值为前两者之和
③重复步骤②,直至只有一颗树为止,这颗树就是哈夫曼树
但是许多时候并不需要真的构建一颗哈夫曼树,最重要的是哈夫曼树的思想,即反复对最小值求和。
例如,《算法笔记》中关于堆箱子的一道题目可以这么写,STL就是强!!
#include
#include
#include
using namespace std;
int main(int argv, char *argc[])
{
priority_queue<long long, vector<long long>, greater<long long>> q;
int arr_length;
long long temp, min1, min2, ans = 0;
scanf_s("%d", &arr_length);
for (int i = 0; i < arr_length; i++)
{
scanf_s("%lld", &temp);
q.push(temp); //压入小顶堆
}
while (q.size() > 1) //若小顶堆中还有两个元素
{
min1 = q.top();
q.pop();
min2 = q.top();
q.pop();
q.push(min1 + min2);
ans += min1 + min2;
}
cout << "最小体力是:" << ans << endl;
return 0;
}
之前打蓝桥杯,图论这一块比较熟悉
邻接矩阵,邻接表,直接上例题
本章开始不写template了,比较耗时间,重点是算法思想
9.1 求无向图G的连通分量数
struct node
{
int weight;
int next;
node(int _n,int _w):weight(_w),next(_n){}
};
const int maxn=10005;
vector<node> G[maxn];
bool vis[maxn];
void dfs(int u)
{
vis[u]=true;
for(int i=0;i<G[u].size();i++)
{
if(vis[G[u][i].next]==false)
dfs(G[u][i].next);
}
}
int trave(int num)
{
int blocks_num=0;
for(int i=0;i<=num;i++)
{
if(vis[i]==false){
dfs(i);
blocks_num++;
}
}
return blocks_num;
}
9.2 求无向图G的边数
法一
int edge_num;//边数,遍历完后除2即可(无向图)
void dfs(int u)
{
vis[u]=true;
for(int i=0;i<G[u].size();i++)
{
edge_num++;
if(vis[G[u][i].next]==false)
{
dfs(G[u][i].next);
}
}
}
法二
//每个结点的邻接表长就是边数
for(int i=0;i<=n;i++)
edge_num+=G[i].size();
//输出/2(无向图)
cout<<edge_num/2<<endl;
9.3 广搜 略
9.4 最小生成树
这里补充一下prim,关于kruskal,移步另一篇博文
#include
#include
using namespace std;
const int maxn=10005;
const int inf=1e7+8;
struct node{
int v;int w;
node(int a,int b):v(a), w(b){
}
};
vector<node > G[maxn];
int d[maxn];bool vis[maxn];
int prim(int s,int n)
{
int ans=0;
d[s]=0; //对应selected_Vset={v0}
fill(d,d+maxn,inf); //对应Init_MinEdges(g,MinEdges,v0)
fill(vis,vis+maxn,false);
for(int i=0;i<n;i++)
{
int u=-1;int min=inf; //对应Get_Min_Edges(g,MinEdges)
for(int j=0;j<n;j++)
{
if(min>d[j]&&vis[j]==false)
{
min=d[j];
u=j;
}
}
if(u==-1) return -1;
vis[u]=true;
ans+=d[u]; //对应selected_Vset=selected_Vset+{k}
for(int j=0;j<G[u].size();j++)//对应change_MinEdges_width(g,MinEdges,k)
{
int v=G[u][j].v;
int w=G[u][j].w;
if(vis[v]==false&&w<d[v])
{
d[v]=w;
}
}
}
return ans;
}
int main()
{
int n,m;cin>>n>>m;
for(int i=0;i<m;i++)
{
int u,v,w;cin>>u>>v>>w;
G[u].push_back(node(v,w));
G[v].push_back(node(u,w));
}
cout<<prim(0,n);
return 0;
}
9.5 拓扑排序
移步另一篇博文
9.6 关键路径
①按拓扑排序序列,依次求各个顶点的Ve(k):
Ve(源点)=0
Ve(k) = Max {Ve(j) + Weight(Vy, Vx)},Vy为Vk 的任意前驱
②按逆拓扑排序序列,依次求各个顶点的Vl(k):
Vl(汇点)=Ve(汇点)
Vl(k) = Min {Vl(j)- Weight(Vk, Vj)} , Vk为Vj的任意后继
③若边<Vk, Vj>表示活动Ai,则有e(i) = Ve(k)
note:k——Ai——>j
④若边<Vk, Vj>表示活动ai,则有l(i) = Vl(j) - Weight(Vk, Vj)
⑤d(i) = l(i)-e(i)
9.7 关键路径
移步这里—>单源最短路径 dijkstra SPFA
10.1概述
(占位)
10.2顺序表的查找
(1)暴力搜索
(2)二分搜索
(3)分块查找索引表
10.3树表
二叉查找树binary search tree,BST是一种特殊的二叉树,又称排序二叉树,二叉搜索树。它的递归定义如下:
①空树是二叉查找树
②由根结点、左子树和右子树组成的二叉树且满足左子树的数据域小于根结点的数据域,右子树的数据域大于根结点的数据域的树是二叉查找树
由于二叉查找树中明确了左右子树的关系,所以,对于二叉查找树的查找的时间复杂度为O(h),其中h为树的高度。下面给出一般性的查找思路:
①如果Root为空,查找失败并返回
②如果待查值与Root的数据域相等,查找成功,返回
③如果待查值小于Root的数据域,往左子树递归
④如果待查值大于Root的数据域,往右子树递归
关于删除,总是优先删除前驱会使查找二叉树退化成链式结构,因此要解决这个问题,可以每次交替删除前后驱,或是记录子树的高度,总是从较高的子树中删除
AVL的调整,掌握思想即可,其中RR和LL比较简单,下面是RL和LR的图,方便忘记的时候回忆
10.4散列查找
①构造方式
(1)直接定址法
(2)除留余数法
(3)平方取中
(4)折叠法
❤ (5)数值分析法
②处理冲突
(1)开放定址法:线性探测法,二次探测法
(2)再散列法
(3)拉链法
(4)折叠法
note:或许要补充一些关于散列查找性能的计算(王道)
关于查找失败的平均查找长度,模拟一下之后找规律手推
note:基于比较的排序的最坏情况[log2(n!)]+1
①带哨兵的插入排序
#include
#include
#include
using namespace std;
void insert_sort(int *arr,int n)
{
for(int i=2;i<=n;i++)//从下标2开始,0是哨兵位置,1是第一个元素位置,第一个元素天然有序
{
arr[0]=arr[i];//指向当前待插入元素
int j=i-1;
while(arr[0]<arr[j]){//与当前待插入元素前的元素逐一比较
//符合插入条件就后移一位
arr[j+1]=arr[j];
j--;
}
//当前待插入元素归位
arr[j+1]=arr[0];
}
}
int main()
{
srand((int)time(0));
int arr[100];
for (int i = 1; i <= 20; i++)
{
arr[i]=rand()%100;
}
insert_sort(arr,20);
for(int i=1;i<=20;i++)
{
cout<<arr[i]<<" ";
}
return 0;
}
②希尔排序
void shell_sort(int *arr,int n)
{
for(int d=n/2;d>=1;d/=2)
{
for(int i=d+1;i<=n;i++)
{
arr[0]=arr[i];
int j=i-d;
while(j>0 && arr[0]<arr[j] ){
arr[j+d]=arr[j];
j-=d;
}
arr[j+d]=arr[0];
}
}
}
③冒泡排序
void bubble_sort(int *arr,int n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n-i+1;j++)//从前往后冒
{
if(arr[j]>arr[j+1])
{
int temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
}
④快速排序
int partition(int * arr,int low ,int high)
{
int base=arr[low];
while (low<high)
{
while (low<high&&arr[high]>=base) high--;
arr[low]=arr[high];
while (low<high&&arr[low]<=base) low++;
arr[high]=arr[low];
}
arr[low]=base;//low==high
return low;
}
void quicksort(int *arr,int low,int high)
{
if(low>=high) return ;
int index=partition(arr,low,high);
quicksort(arr,low,index-1);
quicksort(arr,index+1,high);
}
⑤堆排序
堆排序是选择排序的一种,直接选择排序比较简单
#include
#include
#include
using namespace std;
//小的结点下坠,建立大根堆
void sift_max(int *arr,int root_index,int max_index)
{
arr[0]=arr[root_index];//暂存根结点
for(int i=2*root_index;i<=max_index;i*=2)//--->[2i]~访问子节点(完全二叉树的性质)
{
//比较[2i]和[2i+1]~比较左右子树的大小 并使i指向左右子树较大的一个
if(i<max_index&&arr[i]<arr[i+1]) i++;
//根大,则结束
if(arr[0]>arr[i]) break;
//继续
else
{
arr[root_index]=arr[i];
root_index=i;
}
}
arr[root_index]=arr[0];
}
//大的结点下坠,建立小根堆
void sift_min(int *arr,int root_index,int max_index)
{
arr[0]=arr[root_index];//暂存根结点
for(int i=2*root_index;i<=max_index;i*=2)//--->[2i]~访问子节点(完全二叉树的性质)
{
//比较[2i]和[2i+1]~比较左右子树的大小 并使i指向左右子树较小的一个
if(i<max_index&&arr[i]>arr[i+1]) i++;
//根小,则结束
if(arr[0]<arr[i]) break;
//继续
else
{
arr[root_index]=arr[i];
root_index=i;
}
}
arr[root_index]=arr[0];
}
void heap_sort(int *arr,int length,bool is_max_seq=true)
{
//大根
if(is_max_seq==true){
for(int i=length/2;i>=1;i--)
sift_max(arr,i,length);
for(int i=length;i>=2;i--)
{
int temp=arr[i];
arr[i]=arr[1];
arr[1]=temp;
sift_max(arr,1,i-1);
}
}
//小根
else
{
for(int i=length/2;i>=1;i--)
sift_min(arr,i,length);
for(int i=length;i>=2;i--)
{
int temp=arr[i];
arr[i]=arr[1];
arr[1]=temp;
sift_min(arr,1,i-1);
}
}
}
int main()
{
srand((int)time(0));
int arr[1005];
for (int i = 1; i <= 20; i++)
{
arr[i]=rand()%100;
}
heap_sort(arr,20);
for(int i=1;i<=20;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
heap_sort(arr,20,false);
for(int i=1;i<=20;i++)
{
cout<<arr[i]<<" ";
}
return 0;
}
[Running] cd "c:\Users\RECPET-D\Desktop\dsLearning\" && g++ -std=c++11 m_heapsort.cpp -o m_heapsort && "c:\Users\RECPET-D\Desktop\dsLearning\"m_heapsort
5 13 23 29 38 41 43 49 50 60 61 63 64 78 79 81 83 85 91 97
97 91 85 83 81 79 78 64 63 61 60 50 49 43 41 38 29 23 13 5
[Done] exited with code=0 in 0.203 seconds
⑥归并排序
#include
#include
#include
using namespace std;
int temp_arr[100000];
void merge(int *arr,int mid,int low,int high)
{
for(int k=low;k<=high;k++) temp_arr[k]=arr[k];
int i=low;int j=mid+1;
int cnt=low;
while(i<=mid&&j<=high)
{
if(temp_arr[i]<=temp_arr[j])
arr[cnt++]=temp_arr[i++];
else
arr[cnt++]=temp_arr[j++];
}
while (i<=mid)
arr[cnt++]=temp_arr[i++];
while (j<=high)
arr[cnt++]=temp_arr[j++];
}
void merge_sort(int *arr,int low,int high)
{
if(low>=high) return ;
int mid=(low+high)/2;
merge_sort(arr,low,mid);
merge_sort(arr,mid+1,high);
merge(arr,mid,low,high);
}
int main()
{
srand((int)time(0));
int arr[1005];
int arr_length=20;
for (int i = 1; i <= arr_length; i++)
{
arr[i]=rand()%97;
}
for(int i=1;i<=arr_length;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
merge_sort(arr,1,arr_length);
for(int i=1;i<=arr_length;i++)
{
cout<<arr[i]<<" ";
}
return 0;
}
[Code Runnner for Visual Studio Code]
[Running] cd "c:\Users\RECPET-D\Desktop\dsLearning\ds\" && g++ -std=c++11 m_merge.cpp -o m_merge && "c:\Users\RECPET-D\Desktop\dsLearning\ds\"m_merge
67 42 80 88 69 12 37 14 10 75 55 12 70 65 72 37 54 43 1 22
1 10 12 12 14 22 37 37 42 43 54 55 65 67 69 70 72 75 80 88
[Done] exited with code=0 in 0.297 seconds
6月30第一轮
7月8号补充:第五章递归和第六章的内容,这块不是重点,而且不算难点,属于比较基础的内容
7月12号补充:图的内容,还差个关键路径和最短路问题
7月13号补充:还差最后两章,这两章以前学得就不咋样,理论补完就开始刷题
7月14号补充:只剩排序了,冲冲冲
7月18号补充:完结撒花
开始肝题了,后续会不定期补充一些做题中遇到的问题
byebye~
标题2
标题3
备注1
备注2