持续更新中
导论
(1)数据结构是什么?
研究如何在计算机中存储数据,
表现为:操作,操作的实现以及实现的成本。
(2)分为:数学模型和逻辑模型(也称为抽象数据类型 ADT)
抽象数据类型: 没有具体的实现细节
1、单链表
概念
静态列表 --> 数组
动态列表 --> 链表
// c++ 写法
struct Node{
int data;
Node *next; // 存储下一个节点的地址
} 称为一个节点
Node *temp = new Node();
头结点:作为变量,存储第一个节点的地址
头结点作全局变量 时:注意不要修改头结点, 使用新的节点代替头结点进行操作。
头结点作局部变量: 使用方法进行操作时,需要在函数参数中传头结点,传的是头结点的地址。
局部变量: 在函数执行期间存在
全局变量:在程序运行期间一直存在
数组链表-比较
数组 | 链表 | |
---|---|---|
访问元素 | O(1) 直接访问 | O(n) 需要从头开始遍历 |
插入 从头插入 | O(n) 需要移动元素 | O(1) 直接创建新节点 |
插入 从尾部插 | O(1) 直接插 或者 O(n) 数组没有空余位置需要开辟新空间 | 遍历到尾部 需要O(n) |
插入 从中间插入 | O(n)一般情况下平均需要移动n/2 个位置 | O(n) |
使用 | 简单 | 在c/c++ 中会出现段错误或内存泄漏 |
链表插入(头插法)
// **头插法 head 作全局变量** 注意插入函数的变化
#include<stdio.h>
#include<stdlib.h>
struct Node{
int data ;
Node *next; // 指针域
};
Node *head = new Node(); // 全局变量 头结点,指向空
void Insert(int x){
Node * temp = new Node();
// struct Node *temp = (Node*)malloc(sizeof(struct Node)); // c 语言
temp->data = x;
temp-> next =head; // 不可颠倒顺序
head =temp;
}
void Print(){
Node *temp = head; // 创建新节点 代替 头结点 ,即新节点指向第一个节点。
printf("list is:\n");
while(temp!= NULL){
printf("%d\n",temp->data);
temp = temp->next;
}
}
int main(){
head = NULL;
printf("How many numbers");
int n,i,x;
scanf("%d",&n);
for (i =0;i<n;i++){
printf("Enter the number");
scanf("%d",&x);
Insert(x);
Print();
}
return 0;
}
// head 作局部变量
#include<stdio.h>
#include<stdlib.h>
struct Node{
int data ;
Node *next; // 指针域
};
Node *Insert( Node*head,int x){
Node * temp = new Node();
// struct Node *temp = (Node*)malloc(sizeof(struct Node)); // c 语言
temp->data = x;
temp-> next =head; // 不可颠倒顺序
head =temp;
return head;
}
void Print(Node* head){
printf("list is:\n");
while(head!= NULL){
printf("%d\n",head->data);
head = head->next;
}
}
int main(){
Node *head = NULL; // 局部变量
printf("How many numbers");
int n,i,x;
scanf("%d",&n);
for (i =0;i<n;i++){
printf("Enter the number");
scanf("%d",&x);
head = Insert(head,x); //insert函数 返回头结点地址
Print(head);
}
return 0;
}
结果:
在任意位置插入
需要考虑两点:
在头部插入
在其他位置插入 ,需要找到第n-1 的位置
#include<stdio.h>
#include<stdlib.h>
struct Node{
int data ;
Node *next; // 指针域
};
Node *Insert( Node*head,int d,int x){
//创建新节点
Node * temp = new Node();
temp->data = d;
temp->next =NULL;
// 在第一个位置插入
if(x ==1){
temp-> next =head; // 不可颠倒顺序
head =temp;
return head;
}
Node *pre_node = head; // 指向head
// 找到第n-1 个位置
for(int i=0;i< x-2;i++){
pre_node = pre_node-> next;
}
temp->next = pre_node->next;
pre_node->next=temp;
return head;
}
void Print(Node* head){
printf("list is:\n");
while(head!= NULL){
printf("%d\n",head->data);
head = head->next;
}
}
int main(){
Node *head = NULL; // 局部变量
int n; // 次数
int d,x; //数据 ,位置
printf("Enter the numbers\n");
scanf("%d",&n);
while(n--)
{
printf("Enter data and position for example: 3 1\n");
scanf("%d %d",&d,&x);
head = Insert(head,d,x); //insert函数 返回头结点地址
Print(head);
}
return 0;
}
结果:
删除任意位置的节点
节点是动态内存分配的, 是内存堆的一部分
所以在c/c++ 中必须显示释放内存
#include<stdio.h>
#include<stdlib.h>
struct Node{
int data ;
Node *next; // 指向节点的指针
};
Node *head = NULL; // 全局变量
void Insert( int d,int x){ // d 代表数据 x 代表位置
//创建新节点
Node * temp = new Node();
temp->data = d;
temp->next =NULL;
// 在第一个位置插入
if(x ==1){
temp-> next =head;
head =temp;
return ;
}
Node *pre_node = head; // 指向head
// 找到第n-1 个位置
for(int i=0;i< x-2;i++){
pre_node = pre_node-> next;
}
temp->next = pre_node->next; // 插入节点
pre_node->next=temp;
return ;
}
void Delete(int n){ // n 代表位置
Node *pre_node = head; // 指向head
if(n==1){
head = pre_node->next;
// delete(pre_node);
free(pre_node);
return ; // 易错
}
// 找到第n-1 个位置
for(int i=0;i< n-2;i++){
pre_node = pre_node -> next;
}
Node *temp = pre_node->next;
pre_node->next = temp->next;
// free(temp); // c 语言
delete(temp);
// return;
}
void Print(){
Node *temp= head;
printf("list is:\n");
while(temp!= NULL){
printf("%d\n",temp->data);
temp = temp->next;
}
}
int main(){
// int n; // 次数
// int d,x; //数据 ,位置
// printf("Enter the numbers\n");
// scanf("%d",&n);
// while(n--)
// {
// printf("Enter data and position for example: 3 1\n");
// scanf("%d %d",&d,&x);
// Insert(d,x); //insert函数 返回头结点地址
// Print();
// }
Insert(3,1);
Insert(2,2);
Insert(4,3);
printf("delete form list: enter a position \n");
int dd;
scanf("%d",&dd);
Delete(dd);
Print();
return 0;
}
结果
迭代反转链表
需要设置三个指针,一个保存当前值,一个保存前面,一个保存后面。将指针的指向翻转。
考虑 链表为空的特殊情况!!
#include<stdio.h>
#include<stdlib.h>
struct Node{
int data ;
Node *next; // 指向节点的指针
};
Node *head = NULL; // 全局变量
void Insert( int d,int x){ // d 代表数据
//创建新节点
Node * temp = new Node();
temp->data = d;
temp->next =NULL;
// 在第一个位置插入
if(x ==1){
temp-> next =head; // 不可颠倒顺序
head =temp;
return ;
}
Node *pre_node = head; // 指向head
// 找到第n-1 个位置
for(int i=0;i< x-2;i++){
pre_node = pre_node-> next;
}
temp->next = pre_node->next;
pre_node->next=temp;
return ;
}
void Delete(int n){
Node *pre_node = head; // 指向head
if(n==1){
head = pre_node->next;
// delete(pre_node);
free(pre_node);
return ; // 易错
}
// 找到第n-1 个位置
for(int i=0;i< n-2;i++){
pre_node = pre_node -> next;
}
Node *temp = pre_node->next;
pre_node->next = temp->next;
// free(temp); // c 语言
delete(temp);
// return;
}
void Reverse(){
Node * prev = NULL , *current = head ,*after;
while(current!=NULL){
after = current->next;
current->next =prev;
prev= current;
current = after;
}
head =prev; //重点!!!
}
void Print(){
Node *temp= head;
printf("list is:\n");
while(temp!= NULL){
printf("%d\n",temp->data);
temp = temp->next;
}
}
int main(){
// int n; // 次数
// int d,x; //数据 ,位置
// printf("Enter the numbers\n");
// scanf("%d",&n);
// while(n--)
// {
// printf("Enter data and position for example: 3 1\n");
// scanf("%d %d",&d,&x);
// Insert(d,x); //insert函数 返回头结点地址
// Print();
// }
Insert(3,1);
Insert(2,2);
Insert(4,3);
Print();
// // 删除元素
// printf("delete form list: enter a position \n");
// int dd;
// scanf("%d",&dd);
// Delete(dd);
// Print();
// 翻转链表
Reverse();
Print();
return 0;
}
结果
递归反转遍历链表
递归使用栈空间多次调用函数
#include<stdio.h>
#include<stdlib.h>
struct Node{
int data ;
Node *next; // 指针域
};
Node *Insert( Node*head,int d,int x){
//创建新节点
Node * temp = new Node();
temp->data = d;
temp->next =NULL;
// 在第一个位置插入
if(x ==1){
temp-> next =head; // 不可颠倒顺序
head =temp;
return head;
}
Node *pre_node = head; // 指向head
// 找到第n-1 个位置
for(int i=0;i< x-2;i++){
pre_node = pre_node-> next;
}
temp->next = pre_node->next;
pre_node->next=temp;
return head;
}
void Print(Node* temp){
if(temp == NULL) {
printf("\n");
return;
}
printf("%d",temp->data); // 先输出
Print(temp->next); // 然后递归调用
}
void ReversePrint(Node* temp){
if(temp==NULL) {
printf("\n");
return;
}
ReversePrint(temp->next); // 先一层层的往下进行递归调用
printf("%d",temp->data); // 递归到边界条件后在开始一层层的从下往上输出
}
int main(){
Node *head= NULL;
head = Insert(head,3,1);
head = Insert(head,4,2);
head = Insert(head,5,3);
head = Insert(head,6,4);
Print(head);
printf("reverse list:");
ReversePrint(head);
return 0;
}
结果
递归反转链表
栈中存储的是结构体的地址
#include<stdio.h>
//#include<stdlib.h>
struct Node{
int data ;
Node *next; // 指针域
};
// 全局变量
Node * head = NULL;
//反转链表
void Reverse(Node* p){
if(p->next == NULL){ // 递归出口:head 指向最后一个节点
head = p;
return; // 重要!!
}
Reverse(p->next);
Node * q = p ->next ;
q-> next = p;
p->next = NULL;
}
//插入
void Insert( int d,int x){
//创建新节点
Node * temp = new Node();
temp->data = d;
temp->next = NULL;
// 在第一个位置插入
if(x ==1){
temp-> next =head; // 不可颠倒顺序
head =temp;
return ;
}
Node *pre_node = head; // 指向head
// 找到第n-1 个位置
for(int i=0;i< x-2;i++){
pre_node = pre_node-> next;
}
temp->next = pre_node->next;
pre_node->next=temp;
}
// 递归打印
void Print(Node* temp){
if(temp == NULL) {
printf("\n");
return;
}
printf("%d ",temp->data); // 先输出
Print(temp->next); // 递归调用
}
int main(){
Insert(3,1);
Insert(4,2);
Print(head);
Reverse(head);
Print(head);
return 0;
}
2、双链表
优点: 可以反向查询
双链表实现
#include <stdio.h>
using namespace std;
struct Node{
int data;
Node *prev;
Node *next;
};
Node *head =NULL;
//生成新节点
Node* GetNode(int x){
Node *temp= new Node();
temp->data=x;
temp->prev=NULL;
temp->next=NULL;
return temp;
}
//头插法
void InsertHead(int x){
Node *newNode = GetNode(x);
//链表为空
if(head == NULL)
{
head =newNode ;
return;
}
head ->prev = newNode; // head的pre 指向新节点
newNode ->next = head; //新节点的next指向head
head = newNode;
}
// 尾插法
void InsertTail(int x){
Node* temp = head; // 指向head 的节点
Node *newNode = GetNode(x);
//链表为空
if(head == NULL)
{
head = newNode;
return;
}
while( temp->next != NULL)
temp = temp->next; // 找最后一个节点
temp->next = newNode;
newNode->prev = temp;
}
//打印输出
void Print(){
Node *temp = head;
while( temp != NULL){
printf("%d ",temp->data);
temp = temp->next;
}
}
int main(){
InsertHead(2);
InsertHead(3);
InsertHead(4);
printf(" head List is :\n");
Print(); // 头插法 4 3 2
printf("\n");
InsertTail(7);
InsertTail(6);
InsertTail(5);
InsertTail(4);
printf("tail List is :\n");
Print(); // 4 3 2 7 6 5 4
return 0;
}
3、 栈
应用场景:
(1)实现递归,递归就是函数调用链
(2)文本编辑器或图像编辑器的撤回操作,ctrl +z
(3)编译器使用栈 验证 源码的括号是否匹配,不匹配报错
用 数组 实现栈
边界条件:
数组满时,不能push (往栈中存数据)
push 的时间复杂度最坏为O(n)因为可能数组长度不够,需要重新开辟空间。
数组为空时,不能pop (从栈中去除数据)
#include<stdio.h>
#define size 100
int Stack[size];
int top = -1;
// 数据压栈
void Push(int x){
if(top == size-1){
printf("ERROR: stack overflow !");
return ;
}
top++;
Stack[top]=x;
// Stack[++top] = x;
}
// 弹出栈
void Pop(){
if(top == -1){
printf("ERROR: No element to pop!");
return;
}
top--;
}
//输出栈顶元素
int Top(){
return Stack[top];
}
// 栈是否为空
bool Empty(){
if(top == -1){
return true;
}
}
// 输出栈中元素
void Print(){
printf("Stack is:");
for(int i= 0;i<=top;i++){
printf("%d ",Stack[i]);
}
printf("\n");
}
int main(){
Push(2);
Push(4);
Print();
Pop();
Print();
return 0;
}
结果
用链表实现栈
#include<stdlib.h>
#include<iostream>
#include<stdio.h>
#include<stack>
#include <string.h>
using namespace std;
struct Stack{
int data;
Stack *next;
};
//Stack *top= new Stack();
Stack *top =NULL;
// 链表的头插
void Push(int x){
Stack *temp = new Stack();
// Stack *temp = NULL; // 和上句代码作用不一样
temp ->data = x;
temp ->next = top;
top =temp;
}
// 链表的删除
void Pop(){
Stack *temp = new Stack();
if(top == NULL) return;
temp = top; // 将top节点的地址传给新节点temp ,top 和 temp 指向同一个
top = top ->next;
delete(temp);
}
// 链表的输出
void Print(){
Stack *temp = top; // 创建新节点 ,将top 节点的地址传给新节点
printf("Stack is: \n ");
while(temp != NULL){
printf("%d ",temp->data);
temp = temp->next;
}
}
int main(){
Push(2);
Push(4);
Push(3);
Print();
Pop();
printf("\n");
Print();
return 0;
}
结果
反转字符串
c++ 中自己实现栈,可以用数组或者链表
- 补充
使用c++ 自带的栈
#include< stack >
gets
头文件<stdio.h>
char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
strlen
#include <string.h>
strlen()函数用来计算字符串的长度,其原型为:
unsigned int strlen (char *s);
【参数说明】s为指定的字符串。
strlen()用来计算指定的字符串s 的长度,不包括结束字符"\0"。
【返回值】返回字符串s 的字符数。
#include<stdlib.h>
#include<iostream>
#include<stdio.h>
#include<stack>
#include <string.h>
using namespace std;
// 使用数组 反转字符串
void Reverse(char arr[],int n){
stack<char> s;
// loop for push
for(int i=0;i<n;i++){
s.push(arr[i]);
}
// loop for pop
for(int i= 0;i<n;i++){
arr[i] = s.top();
s.pop();
}
}
int main(){
char arr[12];
printf("Enter a string :");
gets(arr);
Reverse(arr,strlen(arr));
printf(arr);
return 0;
}
#include<stdlib.h>
#include<iostream>
#include<stdio.h>
#include<stack>
#include <string.h>
using namespace std;
// 使用两个变量反转字符串
void Reverse(char arr[],int i ,int j){
while(i<=j){
char temp;
temp = arr[i];
arr[i] = arr[j];
arr[j] =temp;
i++;
j--;
}
}
int main(){
char arr[12];
printf("Enter a string :");
gets(arr);
Reverse(arr,0,strlen(arr)-1);
printf(arr);
return 0;
}
char arr[ ] 等价于char *arr
也可以 通过 Reverse(arr,strlen(arr)); 达到只转换某几个的效果
#include<stdlib.h>
#include<iostream>
#include<stdio.h>
#include<stack>
#include <string.h>
using namespace std;
// 使用栈反转字符串
void Reverse(char *arr,int n ){
stack<char> s;
int i;
for(i=0;i<n;i++)
s.push(arr[i]);
for(i=0;i<n;i++){
arr[i]=s.top();
s.pop();
}
}
int main(){
char arr[12];
printf("Enter a string :");
gets(arr);
Reverse(arr,strlen(arr));
printf(arr);
return 0;
}
结果
反转链表
(1)压入所有的元素
(2)出栈
(3)将最后一个节点指向null
#include<stdlib.h>
#include<iostream>
#include<stdio.h>
#include<stack>
#include <string.h>
using namespace std;
struct Node{
int data;
Node *next;
};
stack<Node *> s;
Node *head =NULL;
// 用栈反转链表
void Reverse(){
// 将结点 压栈
Node * temp = head ;
while(temp!=NULL){
s.push(temp);
temp = temp->next;
}
// 出栈
temp =s.top(); // 指向栈顶
head =temp; // 设置头结点
s.pop();
while(!s.empty()){
temp->next = s.top(); // 实现 next的指针指向反转
s.pop();
temp = temp ->next;
}
temp->next =NULL; // 最后一个节点指向null !!!
}
void Insert(int x){
Node *temp = new Node();
temp ->data=x;
temp ->next = head;
head = temp;
}
void Print(){
Node *temp =head;
while(temp!= NULL){
printf("%d ",temp->data);
temp =temp->next;
}
}
int main(){
Insert(2);
Insert(3);
Insert(4);
printf(" List is :\n");
Print(); // 头插法 4 3 2
Reverse();
printf("\n");
printf(" Recerser List is :\n");
Print(); // 2 3 4
return 0;
}
== 结果==
括号匹配
#include<stdio.h>
#include<stack>
#include <string.h>
using namespace std;
stack<char> sk; // 字符栈
char s[] = "{(([]))}";
// 检查是否匹配
bool check(char l,char r ){
if(l =='('&& r ==')') return true;
if(l =='{'&& r =='}') return true;
if(l =='['&& r ==']') return true;
if(l ==')'&& r =='(') return true;
if(l =='}'&& r =='{') return true;
if(l ==']'&& r =='[') return true;
return false;
}
bool checkBracket(char s[]){
// 循环
// 碰到左括号压入栈中
for(int i=0; i<strlen(s) ;i++){
if(s[i]=='{'||s[i]=='('||s[i]=='[')
sk.push(s[i]);
// 碰到右括号 判断是否 和左括号匹配
// 栈为空,或者不匹配 直接退出
// 否则 弹出 元素,接着检查剩下元素
else if(s[i]==')'||s[i]=='}'||s[i]==']'){
if( sk.empty() || ( !check(s[i],sk.top()) ) ){
return false ;
}
else sk.pop();
}
}
// 最后判断 栈中为空 则匹配成功
return sk.empty()?true:false;
}
int main(){
if(checkBracket(s))
printf(" matching ");
else printf("No matching ");
}
表达式
2 ”^"3 "^ ” 2“ 连续幂成 从右往左算。
a+b的 前缀表达式(+a b), 中缀表达式(a +b),后缀表达式 (a b +)
栈实现后缀表达式求值
从前往后扫描 ,先弹出的是 op2
#include<stdio.h>
#include<stack>
#include <string.h>
using namespace std;
stack<char> sk; // 字符栈
//数字
bool Operand(char c){
if(c>='0'&&c<='9')
return true;
else return false;
}
//字符
bool Operator(char c ){
if(c=='+'||c=='-'||c=='*'||c=='/')
return true;
else return false;
}
//计算
int Perform(char c,int op1,int op2){
int res;
switch(c){
case '+': res = op1+op2; break;
case '-': res = op1-op2; break;
case '*': res = op1*op2; break;
case '/': res = op1/op2; break;
}
return res;
}
//匹配
int Match(char ch[]){
//循环,数字压入栈中,
//碰到符号,则从栈中弹出两个元素,注意弹出顺序
// 计算,将结果压入栈中
int res,op1,op2;
for (int i=0;i<strlen(ch);i++){
if(Operand(ch[i])) sk.push(ch[i]-'0'); // 字符转数字
else if( Operator(ch[i]) ) {
op2=sk.top(); sk.pop(); //先弹出的是第二个数字
op1=sk.top();sk.pop();
res=Perform(ch[i],op1,op2); //计算
sk.push(res); // 结果压入栈中
}
}
return res;
}
int main(){
char ch[] = "23*45*+9-";
printf("%d ",Match(ch));
}
前缀表达式求值
从后往前扫描
碰到数入栈,碰到符号从栈中弹出两个数
先弹出的是第一个操作数
计算的结果压入栈中
举例: 3- 2 的前缀表达式为: - 3 2
先压入 2
在压入 3
碰到 -
先弹出3
在弹出2
#include<stdio.h>
#include<stack>
#include <string.h>
using namespace std;
stack<char> sk; // 字符栈
//数字
bool Operand(char c){
if(c>='0'&&c<='9')
return true;
else return false;
}
//字符
bool Operator(char c ){
if(c=='+'||c=='-'||c=='*'||c=='/')
return true;
else return false;
}
//计算
int Perform(char c,int op1,int op2){
int res;
switch(c){
case '+': res = op1+op2; break;
case '-': res = op1-op2; break;
case '*': res = op1*op2; break;
case '/': res = op1/op2; break;
}
return res;
}
//前缀匹配
int Match(char ch[]){
//从 右向左 循环,数字压入栈中,
//碰到符号,则从栈中弹出两个元素,注意弹出顺序
// 计算,将结果压入栈中
int res,op1,op2;
for (int i= strlen(ch)-1 ;i >= 0;i--){
if(Operand(ch[i])) sk.push(ch[i]-'0');
else if( Operator(ch[i]) ) {
op1=sk.top(); sk.pop();
op2=sk.top();sk.pop();
res=Perform(ch[i],op1,op2);
sk.push(res);
}
}
return res;
}
int main(){
char ch[] = "-+*23*549";
printf("%d ",Match(ch));
}
中缀表达式变后缀表达式
没有括号的伪代码
有括号的伪代码
4、队列
与栈的在某一端插入删除不同
队列必须是在一端插入,另一端删除。
用数组表示队列
没有 考虑数组大小 的伪代码
考虑数组大小 (即 使用循环数组) 的伪代码
#include<stdlib.h>
#include<stdio.h>
#define N 10
// 用循环数组实现队列
int queue[N];
int front=-1 ,rear=-1;
//判断是否为空
bool Isempty(){
if(front==-1 && rear==-1){
return true;
}
return false;
}
//出队列
void Dequeue(){
if(Isempty()) return;
else if(front == rear) front =rear = -1;
else {
front = (front+1)%N; // 重点!!!!
}
}
//入队列
void Enqueue(int x){
if((rear+1)%N==front) return ;
else if(Isempty()){
front = rear =0;
}
else rear=(rear+1)%N;
queue[rear]=x;
}
//队首元素
void Front(){
printf("%d ",queue[front]);
}
void Print(){
for(int i=front ;i<=rear ;i++){
printf("%d ",queue[i]);
}
}
int main(){
Enqueue(1);
Enqueue(2);
Enqueue(3);
Enqueue(4);
Print(); // 入队 1 2 3 4
printf("\n");
Front(); // 队首 1
printf("\n");
Dequeue();
Print(); // 2 3 4
return 0;
}
用链表表示队列
用数组实现时,当数组满时为了插入元素需要开辟新的空间,复杂度O(n)
当数组没有元素时,内存资源被浪费
- 补充
注意 : c语言 中 if (0)之后的代码是不执行的
C 或 C++ 中 输出 bool 变量的值 ,是用数字 1 和 0 表示,而不是 true 或 false。
Java、PHP、JavaScript 等也都支持布尔类型,但输出结果为 true 或 false。
#include<stdlib.h>
#include<stdio.h>
// 用链表实现队列
struct queue{
int data;
queue *next;
};
queue *front =NULL;
queue *rear =NULL;
//判断是否为空
bool Isempty(){
if(front==NULL && rear==NULL){
return true;
}
return false;
}
//出队列
void Dequeue(){
queue *temp = front;
//对空 if(Isempy) return ; 错误
if (front== NULL) return; // 重点
//只剩一个元素
if(front ==rear) front =rear= NULL;
// 出队
else {
front =front->next;
}
delete(temp);
}
//入队列
void Enqueue(int x){
// 创建新节点
queue *temp = new queue();
temp->data=x;
temp->next=NULL;
//第一次入队
if(front ==NULL && rear ==NULL)
front =rear =temp;
// 入队
rear->next=temp;
rear =temp;
}
//对首元素
void Front(){
printf("%d ",front->data);
}
void Print(){
queue *temp=front;
while(temp!=NULL){
printf("%d ",temp->data);
temp= temp->next;
}
}
int main(){
Enqueue(1);
Enqueue(2);
Enqueue(3);
Enqueue(4);
Enqueue(5);
Enqueue(6);
Print(); // 入队 1 2 3 4 5 6
printf("\n");
Dequeue(); // 出队 2 3 4 5 6
Print();
printf("\n");
Front(); // 2
printf("\n");
printf("%d ",Isempty());
return 0;
}
5、树
-
树是用来表示层次数据的一个结构
遍历一棵树只能从一个方向进行
兄弟必须有一个双亲
树是递归的数据结构(子树) -
应用:
磁盘驱动器的文件系统组织数据,二叉搜索树快速查找
做数据字典,动态拼写检查
网络路由算法
二叉排序树
字符串按字典排序或者按字母排序进行查找
二叉搜索树
判断是否是二插搜索树
方法 1 可以中序遍历判断是否有序。
方法2 递归判断左子树是否小于根节点,右子树是否大于根节点,当前是否是二叉搜索树。
递归插入与查找
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
// 二叉树 想双链表
struct BstNode
{
int data ;
BstNode *left;
BstNode *right;
};
// 创建新节点
BstNode *GetNode(int data){
BstNode * temp = new BstNode();
temp->data = data;
temp->left = temp->right = NULL;
return temp;
}
// 递归实现插入
BstNode* Insert( BstNode * root ,int data){
// 树为空,设置根节点
if(root == NULL){
root = GetNode(data);
}
// 树不空,小于则 插左边
// 树不空,大于 插右边
else if(data <= root->data) {
root->left = Insert(root->left ,data);
}
else if(data >= root->data) {
root->right= Insert(root->right ,data);
}
return root; // root 是局部变量
}
// 递归实现搜索
bool Search( BstNode * root ,int data){
if(root == NULL){
return false;
}
else if(data == root->data) {
return true;
}
else if(data <= root->data) {
return Search(root->left ,data);
}
else if(data >= root->data) {
return Search(root->right ,data);
}
}
int main(){
BstNode *root =NULL; // 局部变量
root = Insert(root ,15);
root = Insert(root ,10);
root = Insert(root ,20);
int number;
cout << "Enter number to search ";
cin >> number;
if(Search(root,number)==true)
printf("found");
else printf("No found");
}
找最值(迭代和递归)
最大:查找右边的子树
最小值:找最左边的子树
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
struct BstNode
{
int data ;
BstNode *left;
BstNode *right;
};
// 创建新节点
BstNode *GetNode(int data){
BstNode * temp = new BstNode();
temp->data = data;
temp->left = temp->right = NULL;
return temp;
}
// 递归实现插入
BstNode* Insert( BstNode * root ,int data){
// 树为空,设置根节点
if(root == NULL){
root = GetNode(data);
}
// 树不空,小于则 插左边
// 树不空,大于 插右边
else if(data <= root->data) {
root->left = Insert(root->left ,data);
}
else if(data >= root->data) {
root->right= Insert(root->right ,data);
}
return root; // root 是局部变量
}
// 迭代 找最小值
int FindMin(BstNode *root){
// 树为空
if(root == NULL) {
cout << " Error caused by the empty tree!"<< endl;
return -1;
}
// 树不空
while(root->left != NULL){
root = root->left;
}
return root->data;
}
// 递归找最小值
int ReFindMin(BstNode *root){
// 边界
if(root == NULL) {
cout << " Error caused by the empty tree!"<< endl;
return -1 ;
}
else if(root->left == NULL){
return root->data;
}
return ReFindMin(root->left);
}
// 递归找最大值
int ReFindMax(BstNode *root){
// 边界
if(root == NULL) {
cout << " Error caused by the empty tree!"<< endl;
return -1 ;
}
else if(root->right == NULL){
return root->data;
}
return ReFindMax(root->right);
}
int main(){
BstNode *root =NULL; // 局部变量
root = Insert(root ,15);
root = Insert(root ,23);
root = Insert(root ,20);
root = Insert(root ,8);
root = Insert(root ,12);
root = Insert(root ,2);
cout << "min :" << FindMin(root) << endl;
cout << "min :" << ReFindMin(root) << endl;
cout << "max : "<< ReFindMax(root) << endl;
}
二叉搜索树的删除
删除叶子节点 直接删
删除 非叶子节点
– 当有一个孩子
– 有两个孩子 变成有一个节点的情况 — 通过找右子树的最小值或者左子树的最大值
#include <iostream>
using namespace std;
struct BstNode
{
int data ;
BstNode *left;
BstNode *right;
};
// 创建新节点
BstNode *GetNode(int data){
BstNode * temp = new BstNode();
temp->data = data;
temp->left = temp->right = NULL;
return temp;
}
// 递归实现插入
BstNode* Insert( BstNode * root ,int data){
// 树为空,设置根节点
if(root == NULL){
root = GetNode(data);
}
// 树不空,小于则 插左边
// 树不空,大于 插右边
else if(data <= root->data) {
root->left = Insert(root->left ,data);
}
else if(data >= root->data) {
root->right= Insert(root->right ,data);
}
return root; // root 是局部变量
}
//找到最小值 ,返回地址
BstNode *FindMin(BstNode *temp){
while(temp->left != NULL){
temp = temp->left;
}
return temp;
}
//删除一个节点
BstNode * Delete(BstNode *root ,int data){
if(root == NULL) return NULL;
// 要删除的数据小于根节点,找左子树
else if (data < root->data) root->left = Delete(root->left, data);
// 要删除的数据大于根节点,找右子树
else if (data > root->data) root->right = Delete(root->right,data);
// 删除数据
else {
// 没有孩子
if(root->left == NULL && root->right == NULL){
delete root;
root = NULL;
}
// 有一个孩子
else if(root->left == NULL){
// 右孩子变成根节点
BstNode *temp = root;
root = root->right;
delete temp;
}
else if(root->right == NULL){
// 指向左孩子
BstNode *temp = root;
root = root->left;
delete temp;
}
// 有两个孩子
else{
// 找到右孩子的最小值 或者找左子树最大值
BstNode *temp = FindMin(root->right);
// 将最小值赋到root
root ->data = temp->data;
// 删除最小值
root->right = Delete(root->right,temp->data);
}
}
return root;
}
// 前序遍历
void PreOrder(BstNode *root){
// 边界
if(root == NULL) {
return ;
}
cout << root->data<< " ";
PreOrder(root->left);
PreOrder(root->right );
}
int main(){
BstNode *root =NULL; // 局部变量
root = Insert(root ,15);
root = Insert(root ,23);
root = Insert(root ,20);
root = Insert(root ,8);
root = Insert(root ,12);
root = Insert(root ,2);
cout << " PreOrder is :";
PreOrder(root) ;
cout << endl;
Delete(root,15) ;
cout << " NewOrder is :";
PreOrder(root) ;
}
二叉树的中序后继节点
二叉树高度
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
struct BstNode
{
int data ;
BstNode *left;
BstNode *right;
};
// 创建新节点
BstNode *GetNode(int data){
BstNode * temp = new BstNode();
temp->data = data;
temp->left = temp->right = NULL;
return temp;
}
// 递归实现插入
BstNode* Insert( BstNode * root ,int data){
// 树为空,设置根节点
if(root == NULL){
root = GetNode(data);
}
// 树不空,小于则 插左边
// 树不空,大于 插右边
else if(data <= root->data) {
root->left = Insert(root->left ,data);
}
else if(data >= root->data) {
root->right= Insert(root->right ,data);
}
return root; // root 是局部变量
}
// 比大小
int Max(int a,int b){
return a >= b ? a : b;
}
// 递归找高度
int ReFindHeight(BstNode *root){
// 边界
if(root == NULL) {
return 0 ;
}
return Max( ReFindHeight(root->left) , ReFindHeight(root->right)) + 1;
}
int main(){
BstNode *root =NULL; // 局部变量
root = Insert(root ,15);
root = Insert(root ,23);
root = Insert(root ,20);
root = Insert(root ,8);
root = Insert(root ,12);
root = Insert(root ,2);
cout << "height :" << ReFindHeight(root) << endl;
}
树的遍历
分为广度遍历和深度遍历
深度遍历又分为 前序,中序,后序遍历
树的广度遍历
用队列 实现链表
当节点出队时 保证 该节点的左右节点都入队
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
struct BstNode
{
int data ;
BstNode *left;
BstNode *right;
};
// 创建新节点
BstNode *GetNode(int data){
BstNode * temp = new BstNode();
temp->data = data;
temp->left = temp->right = NULL;
return temp;
}
// 递归实现插入
BstNode* Insert( BstNode * root ,int data){
// 树为空,设置根节点
if(root == NULL){
root = GetNode(data);
}
// 树不空,小于则 插左边
// 树不空,大于 插右边
else if(data <= root->data) {
root->left = Insert(root->left ,data);
}
else if(data >= root->data) {
root->right= Insert(root->right ,data);
}
return root; // root 是局部变量
}
// 时间复杂度o(n) 空间复杂度 O(1) <-- 一个分支 o(n) <-- 其他分支
void LevelOrder(BstNode *root){
// 边界
if(root == NULL) {
return ;
}
queue<BstNode *> qe;
// 根元素入队
qe.push(root);
while(!qe.empty()){
BstNode *cur = qe.front();
// 输出值
if(cur != NULL) cout << cur->data << " ";
// 左孩子入队
if(cur->left != NULL) qe.push(cur->left);
// 右孩子入队
if(cur->right != NULL) qe.push(cur->right);
// 从队中弹出根元素
qe.pop();
}
}
int main(){
BstNode *root =NULL; // 局部变量
root = Insert(root ,15);
root = Insert(root ,23);
root = Insert(root ,20);
root = Insert(root ,8);
root = Insert(root ,12);
root = Insert(root ,2);
cout << " level order is: "<< endl;
LevelOrder(root);
}
树的深度遍历
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
struct BstNode
{
int data ;
BstNode *left;
BstNode *right;
};
// 创建新节点
BstNode *GetNode(int data){
BstNode * temp = new BstNode();
temp->data = data;
temp->left = temp->right = NULL;
return temp;
}
// 递归实现插入
BstNode* Insert( BstNode * root ,int data){
// 树为空,设置根节点
if(root == NULL){
root = GetNode(data);
}
// 树不空,小于则 插左边
// 树不空,大于 插右边
else if(data <= root->data) {
root->left = Insert(root->left ,data);
}
else if(data >= root->data) {
root->right= Insert(root->right ,data);
}
return root; // root 是局部变量
}
// 前序遍历
void PreOrder(BstNode *root){
// 边界
if(root == NULL) {
return ;
}
cout << root->data<< " ";
PreOrder(root->left);
PreOrder(root->right );
}
//中序遍历
void InOrder(BstNode *root){
// 边界
if(root == NULL) {
return ;
}
PreOrder(root->left);
cout << root->data<< " ";
PreOrder(root->right );
}
//后序遍历
void PostOrder(BstNode *root){
// 边界
if(root == NULL) {
return ;
}
PreOrder(root->left);
PreOrder(root->right );
cout << root->data<< " ";
}
int main(){
BstNode *root =NULL; // 局部变量
root = Insert(root ,15);
root = Insert(root ,23);
root = Insert(root ,20);
root = Insert(root ,8);
root = Insert(root ,12);
root = Insert(root ,2);
cout << " PreOrder is :";
PreOrder(root);
cout << endl;
cout << " InOrder is :";
InOrder(root) ;
cout << endl;
cout << " PostOrder is :";
PostOrder(root) ;
}
6、图
概念
树 | 图 |
---|---|
节点之间有关系 | 任意方式链接 |
n个顶点,n-1个边 | 有序对,无序对 |
有序对 | 无序对 |
---|---|
(a,b) != (b,a) 单向连接 | {a,b} == {b,a} 双向连接 |
树单向连接 | 网络爬虫 |
稀疏图 | 邻接矩阵 |
---|---|
稠密图 | 邻接表 |
有向图–强连接–从任意一个节点到另一个节点都有边–市内交通网络