一、绪论
程序=数据结构+算法
注意O和Ω两者的区分,最后归一为θ
化简方法
核心在于max或者相乘
二、线性表
顺序表理解成数组
可用指针来做,难点在于开空间
List L;
L=(List)malloc(sizeof(List)8*maxsize);
顺序表学习代码主要功能实现
#include<stdio.h>
#include<stdlib.h>//malloc、free函数的头文件
#define Initsize 10 //默认的最大长度
#include<iostream>
using namespace std;
//顺序表的创建
//固定空间
/*typedef struct list
{
int data[Initsize];
int length;
}list;
void initlist(list &L)//这里传进来的一定得是地址
{
L.length=0;
for(int i=0;i<Initsize;i++)
L.data[i]=0;
}
int main()
{
list L;
initlist(L);
for(int i=0;i<Initsize;i++)
cout<<L.data[i]<<endl;
}*/
//动态空间(好处在于后期可进行更改)
/*
typedef struct
{
int *elem;
int length;
int maxsize;//为了更改需要随时记录
} list;
void initlist(list &L)//&为引用 ,与指针效果相当,对实参本身进行操作
{
L.elem=(int *)malloc(sizeof(int)*Initsize);
for(int i=0;i<Initsize;i++)
L.elem[i]=0;
L.length=0;
L.maxsize=Initsize;
}
void increaselist(list &L)
{
int *p=L.elem;//先把原来的元素保留起来
L.elem=(int *) malloc(sizeof(int)*(Initsize+10));
for(int i=0;i<Initsize;i++)
L.elem[i]=p[i];
L.length=Initsize;
L.maxsize=Initsize+10;
free(p);
}
int main()
{
list L;
initlist(L);
increaselist(L);
cout<<L.maxsize;
}*/
//初始化要点:开空间,赋值,把list里的每个元素对应更新
typedef struct
{
int *data;
int length;
} list;
void initlist(list &L)//&为引用 ,与指针效果相当,对实参本身进行操作
{
L.data=(int *)malloc(sizeof(int)*Initsize);
for(int i=0;i<Initsize;i++)
L.data[i]=0;
L.length=0;
}
//插入操作关键点是两个特判和位置后移
void insertlist(list &L,int i,int elem)
{
if(L.length>Initsize)//表满
return ;
if(i<1||i>Initsize)//非位
return ;
for(int j=L.length;j>=i;j--)
L.data[j]=L.data[j-1]; //若是数组越界直接跳过这一步
L.data[i-1]=elem;
L.length++;
}
//删除关键点也是特判和位置前移
void deletelist(list &L,int i,int &e)//加个引用便于把函数返回
{
if(i>Initsize||i<1)
return ;
e=L.data[i-1] ;
for(int j=i-1;j<Initsize-1;j++)
L.data[j]=L.data[j+1];//最后一位其实并没删掉,只是读取的时候length读不到那里
L.length--;
}
int main()
{
list L;
initlist(L);
/*insertlist(L,10,10);*/
//插入测试
/*int e;//存储返回值
for(int i=0;i<Initsize;i++)
insertlist(L,i+1,i+1);
deletelist(L,10,e);
for(int i=0;i<L.length;i++)
cout<<L.data[i]<<endl;
cout<<e<<endl;*/
//删除测试
//线性表共性,先特判再办事
}
插入的平均移动元素次数为n/2;删除为(n-1)/2
链表学习代码主要功能实现
链表中第一个结点的存储位置叫做头指针,头指针和头结点不同,头结点即第一个结点,头指针是指向第一个结点的指针。链表中可以没有头结点,但不能没有头指针,如果链表有头结点,那么头指针就是指向头结点数据域的指针(头结点数据域为空也可以放个长度啥的只不过初始化的时候要搞一下)。
#include <iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
typedef struct node
{
int data;
struct node *next;
}list, * linklist;
//这里使用带头结点的吧
void initlist(linklist &L)
{
L=(list *)malloc(sizeof(linklist));
L->next=NULL;
}
//其他多余操作不需要加;
bool isempty(linklist &L)
{
if(L->next==NULL)
return true;
else
return false;
}
//对于位序插入,要先找到节点的位置
void insertlist(linklist &L,int i,int e)
{
linklist p,q;
int j=0;
p=L;//L存的是头结点的指针,不能拿来直接循环
while(p!=NULL&&j<i-1)
{
p=p->next;
j++;
}
if(p==NULL)
return ;
//p指向的是要插入的前一个
q=(list *)malloc(sizeof(linklist));
q->data=e;
q->next=p->next;
p->next=q;
}
//注意细节的处理
//若是不带头结点在i==1时单独说明
void deletelist(linklist& L,int i,int &e)
{
linklist p,q;
int j=0;
p=L;
while(p!=NULL&&j<i-1)
{
p=p->next;
j++;
}
if(p==NULL||p->next==NULL)
return;
e=p->next->data;
q=p->next;
p->next=q->next;
free(q) ;
}
//插入删除时间复杂度均为O(1)
void output(linklist& L)
{
linklist p=L->next;
while(p!=NULL)
{
cout<<p->data;
p=p->next;
}
}
int main()
{
linklist L;
initlist(L);
for(int i=1;i<10;i++)
insertlist(L,i,i);
int e;
deletelist(L,5,e);
output(L);
cout<<endl<<e;
}
本文使用的为有头结点,其他完整版可参考数据结构总复习
其他思想:
- 逆置
头插到原来的头结点之下实现倒置
-
双链表:使用前后指针
重点在插入,先接后一个再接前一个
删除时只需要接住一个前一个后 -
循环链表
判空条件:L->next=L;
三、堆栈
主要在于栈顶的标定,有一些令top=-1,有一些令top=base
顺序存储
#include <iostream>
#include<stdio.h>
#include<string.h>
#define maxsize 10
#include<stdlib.h>
using namespace std;
typedef struct
{
int data[maxsize];
int top;
}stack;
void initstack(stack &s)
{
s.top=-1;
}
void push(stack &s,int e)
{
if(s.top==maxsize-1)
return;
s.data[s.top+1]=e;
s.top++;
}
void pop(stack &s,int &e)//不要忘了往里传地址
{
if(s.top==-1)
return;
e=s.data[s.top];
s.top--;
}
//比较简单,先判空,再处理一下数组尾部
int main()
{
stack s;
initstack(s);
for(int i=0;i<maxsize;i++)
push(s,i);
int e;
pop(s,e);
pop(s,e);
cout<<e;
}
链式存储
若尾部作为栈顶,找不到前继位,无法pop
代码
#include <iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
typedef struct stac
{
int data;
struct stac *next;
}node,*stack;
//无头结点
void initstack(stack &s)
{
s=NULL;//
}
void push(stack& s,int e)
{
stack q;
q=(stack) malloc(sizeof(stack));
q->data=e;
q->next=s;
s=q;
}
//开空间,赋值,换头
void pop(stack& s,int &e)
{
if(s==NULL)
return;
else
{
stack p;
p=s;
e=p->data;
s=s->next;
free(p);
}
}
//判空,开替代,删除
int main ()
{
stack s;
initstack(s);
for(int i=0;i<5;i++)
push(s,i);
int e;
for(int i=0;i<5;i++)
{
pop(s,e);
cout<<e;
}
}
四、队列
顺序存储结构时可以让front 和 rear同为-1,之后front在实际位置的前一个,也可以同为0,然后rear在实际位置后
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#define maxsize 100
using namespace std;
//顺序存储结构时可以让front 和 rear同为-1,之后front在实际位置的前一个
//也可以同为0,然后rear在实际位置后
typedef struct quene
{
int data[maxsize];
int front,rear;
}quene;
void initquene(quene &q)
{
q.rear=q.front=0;
}
void inquene(quene &q,int e)
{
if((q.rear+1)%maxsize==q.front)
//如果放满的话,rear==front,而队列为空时也存在这个关系,因此只放n-1个
return ;
q.data[q.rear]=e;
q.rear=(q.rear+1)%maxsize;
}
void outquene(quene &q,int &e)
{
if(q.front==q.rear)
return ;
e=q.data[q.front];
q.front =(q.front+1)%maxsize;
}
// 关键点:判断加更新
int main()
{
quene q;
initquene(q);
for(int i=0;i<5;i++)
inquene(q,i);
int e;
for(int i=0;i<5;i++)
{
outquene(q,e);
cout<<e;
}
}
链式存储
不加头结点的时候,rear和front指向最后一个和第一个元素实际位置,不存在前置后置
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<bits/stdc++.h>
#define maxsize 100
using namespace std;
typedef struct node
{
int data;
struct node *next;
} *qnode;
typedef struct quenee
{
qnode front,rear;
}* quene;
//创建的时候注意,rear和front都是节点
void initquene(quene &q)
{
q=(quene)malloc(sizeof(quene));
q->front=NULL;
q->rear=NULL;
}
void inquene(quene &q,int e)
{
qnode p;
p=(qnode)malloc(sizeof(qnode));
p->data=e;
p->next = NULL;//不要忘了这一步
//之后吧这一节点放到链表里面
if(q->rear==NULL)
q->rear=q->front=p;
else
{
q->rear->next=p;
q->rear=p;
}
}
//建结点,分情况赋值
void outquene(quene &q,int &e)
{
if(q->front==NULL)
return;
qnode p;
p=q->front;
e=p->data;
q->front=q->front->next;
free(p);
}
//找替身,操作,删除
int main()
{
quene q;
initquene(q);
for(int i=0;i<5;i++)
inquene(q,i);
int e;
for(int i=0;i<5;i++)
{
outquene(q,e);
cout<<e;
}
}