绪论
基本概念和术语
数据 在计算机科学中是指所有能输入到计算机中并被计算机程序处理计算机程序处理的符号的总称。
数据元素 数据的基本单位
数据项 一个数据元素里由若干个数据项组成
数据对象 性质相同的数据元素集合。
例如,一整张表格就属于数据,表格每一行的数据就是一个数据元素,每行里像性别、初试成绩、录取情况等内容就是数据项,由每行数据元素组成的集合,便是数据对象。
序号 | 姓名 | 性别 | 初试成绩 | 录取情况 |
---|---|---|---|---|
1 | xxx | 男 | 400 | 拟录取 |
2 | xxx | 女 | 380 | 拟录取 |
3 | xxx | 男 | 350 | 拟录取 |
4 | xxx | 男 | 350 | 拟录取 |
5 | xxx | 男 | 320 | 拟录取 |
算法与算法分析
int fun(int n)
{ int i,j,k,s;
s=0;
for(i=0;i<=n;i++)
for(j=0;j<=i;j++)
for(k=0;k<=j;k++)
s++;
return(s);
}
线性表
顺序表(静态)
key:
- 注意代码健壮性。可以在实现了功能之后再考虑用户在传非法数据时引起的健壮性问题。
- 为什么在参一个List时,要写成LinkList &L而不是把&写成*?
代码:
#include <stdio.h>
#define maxsize 10
typedef struct{
int data[maxsize];
int length;
} Sqlist;
//1.为什么加&而不是*
/*
因为插入与删除功能时需要把修改的内容返回,所以加&是为了传递接表的指针。
*/
void initLinarList(Sqlist &L){
for(int i=0;i<10;i++){
L.data[i]=0;
}
L.length=0;
}
//插入节点
bool insertNode(Sqlist &L,int pos,int val){
if(pos<1 || pos>L.length+1 || pos >=maxsize){ //判断是否为合法输入
return false;
}
/******注意******/
/*这里的循环当时写错了,手写时不能模拟,一定要分清楚终止条件与循环条件!*/
//前面的元素往后移
for(int i=L.length;i>=pos;i--){ //i指向链表最后一个元素下标
L.data[i]=L.data[i-1];
}
L.data[pos-1]=val;
L.length++;
return true;
}
//删除节点
bool delNode(Sqlist &L,int pos){
int p=pos-1; //p指向链表最后一个元素的后继下标
if(pos<1 || pos >L.length){ //判断是否为合法输入
return false;
}
while(p<L.length){ //前面的元素往后移
L.data[p]=L.data[p+1];
p++;
}
L.length-=1;
return true;
}
//按位查找
int getItem(Sqlist &L,int pos){
if(pos>0 && pos <=L.length){
return L.data[pos-1];
}else{
return -1;
}
}
int main()
{
/* 顺序表--静态实现方式 */
Sqlist L;
initLinarList(L);
insertNode(L,1,1);
insertNode(L,2,2);
insertNode(L,3,3);
insertNode(L,4,4);
insertNode(L,5,5);
insertNode(L,6,6);
insertNode(L,7,7);
delNode(L,3);
printf("第3位元素:%d\n",getItem(L,3));
printf("顺序表长度:%d\n",L.length);
for(int i=0;i<L.length;i++){
printf("num:%d\n",L.data[i]);
}
return 0;
}
循序表(动态)
key:
- malloc用法
代码:
#include <stdio.h>
typedef struct{
int *head;
int length;
int maxsize;
} Sqlist;
//初始化表
void initLinarList(Sqlist &L){
L.head=(int *)malloc(10*sizeof(int)); //初始分配10个整数
L.maxsize=10;
}
//扩展大小
void increaseSize(Sqlist &L,int size){
if(size<=0){
return;
}
int *p=L.head;
L.head=(int *)malloc((L.maxsize+size)*sizeof(int)); //申请一片连续的空间
for(int i=0;i<L.length;i++){ //复制内容到新区域
L.head[i]=p[i];
}
L.maxsize+=size;
free(p);
}
//插入节点
bool insertNode(Sqlist &L,int pos,int val){
int p=L.length; //p指向链表最后一个元素的后继下标
if(pos<1 || pos >p+1){ //判断是否为合法输入
return false;
}
while(p>=pos ){ //前面的元素往后移
L.node[p]=L.node[p-1];
p--;
}
L.node[p]=val;
L.length+=1;
return true;
}
//删除节点
bool delNode(Sqlist &L,int pos){
int p=pos-1; //p指向链表最后一个元素的后继下标
if(pos<1 || pos >L.length){ //判断是否为合法输入
return false;
}
while(p<L.length){ //前面的元素往后移
L.node[p]=L.node[p+1];
p++;
}
L.length-=1;
return true;
}
//修改元素
bool changeItem(Sqlist &L,int pos,int val){
if(pos>0 && pos <=L.length){
L.node[pos-1]=val;
return true;
}else{
return false;
}
}
//按位查找
int getItem(Sqlist &L,int pos){
if(pos>0 && pos <=L.length){
return L.node[pos-1];
}else{
return -1;
}
}
int main()
{
/* 顺序表--动态态实现方式 */
return 0;
}
单链表(不带头结点)
key:
-
在单链表中,给定一个结点p,如何执行下列操作:
- 在结点p前插入一个结点
- 删除结点p
-
传引用和传指针有什么区别?
-
好像有点懂了。网上说,引用指定过一次后,就不能再指定了,而指针可以随时改指向其他地址。
LinkList &L:传进来的时候是传的指针,后面改头结点的时候,当然可以执行 L=s; 让头结点指向新的地址。
LNode *p:指针L传进来后赋值给 *p,变成了引用?所以当执行 p=s; 时实际p的指向没有变化。
那么问题又来了?我什么时候传的是指针,什么时候是传引用?什么时候声明的是指针?什么时候是引用?
参考:https://www.jb51.net/article/137167.htm
-
代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode{
int data;
struct LNode *next;
}LNode, *LinkList;
//初始化一个不带头结点的单链表
bool initLinkList(LinkList &L){
L=NULL;
return true;
}
//按位序插入
bool insertNodeByPos(LinkList &L,int pos,int val){
int i=1;
LNode *p=L;
if(pos<=0){
return false;
}
if(pos==1){
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=val;
if(L==NULL){ //为空直接添加节点
s->next=NULL; //如果不加,非常有可能出现后继节点存在脏数据
L=s;
}else{ //不为空则连接后面节点替换第一个节点
s->next=p;
L=s; //疑问:为什么这里写p=s是不行得 写L=s才可以
} //--学长说引用和指针不同
return true;
}
while(i<pos-1){
i++;
p=p->next;
}
if(p==NULL){
return false;
}
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=val;
s->next=p->next;
p->next=s;
return true;
}
//节点前插入
bool insertPreNode(LinkList &L,LNode *p,int val){
LNode *s=(LNode *)malloc(sizeof(LNode));
if(p==L){ //在第一个元素前插入
s->data=val;
s->next=p;
L=s;
}else{
s->next=p->next;
p->next=s;
s->data=p->data;
p->data=val;
}
}
//节点后插入
bool insertSucNode(LinkList &L,LNode *p,int val){
LNode *s=(LNode *)malloc(sizeof(LNode));
s->next=p->next;
p->next=s;
s->data=val;
}
//按位序删除
bool delNodeByPos(LinkList &L,int pos){
if(pos<=0 || L==NULL){
return false;
}
int i=1;
LNode *p=L;
if(pos==1){
LNode *q=L;
L=q->next;
free(q);
}
while(p!=NULL){
if(i==pos-1){
LNode *q=p->next;
p->next=q->next;
free(q);
}
i++;
p=p->next;
}
}
//按值查找
LNode * LocateNode(LinkList L,int val){
LNode *p=L->next; //跳过头结点
while(p!=NULL && p->data!=val){
p=p->next;
}
return p;
}
//删除某节点
bool delNode(LNode *s){
if(s==NULL){
return false;
}
LNode *p=s->next;
s->data=s->next->data; //移花接木
if(s->next!=NULL){
s->next=p->next;
}
free(p);
return true;
}
LNode * getNodeByPos(LinkList &L,int pos){
if(pos<=0){
return NULL;
}
int i=1;
LNode *p=L; //指向第一个元素
while(p!=NULL && i<pos){
i++;
p=p->next;
}
return p;
}
//头插法建表
LinkList List_HeadInsert(LinkList &L){
int val;
L=(LinkList)malloc(sizeof(LNode));
LNode *s,*p=L;
scanf("%d",&val); //建立第一个指针
p->data=val;
p->next=NULL; //p后继指向空
scanf("%d",&val);
while(val!=99999){
s=(LNode *)malloc(sizeof(LNode));
s->data=val;
s->next=p;
L=s; //新插入元素成为第一个结点 这里再次体现p与L的实质是不一样的
p=L; //p指向第一个结点
scanf("%d",&val);
}
return L;
}
//尾插法建表
LinkList List_TailInsert(LinkList &L){
int val;
LNode *r,*s,*p;
scanf("%d",&val);
r=(LNode *)malloc(sizeof(LNode)); //声明尾指针
r->data=val;
p=r;
scanf("%d",&val);
while(val!=99999){
s=(LNode *)malloc(sizeof(LNode));
s->data=val;
r->next=s;
r=s;
scanf("%d",&val);
}
r->next=NULL; //尾结点置空
return p;
}
/*单链表---不带头结点*/
int main(){
LinkList L;
LinkList L1;
LinkList L2;
initLinkList(L);
insertNodeByPos(L,1,5);
insertNodeByPos(L,1,3);
insertNodeByPos(L,1,2);
insertNodeByPos(L,1,1);
insertPreNode(L,L->next->next->next,4);
insertSucNode(L,L->next->next->next->next,6);
delNode(L);
delNodeByPos(L,2);
LNode *l=getNodeByPos(L,1);
printf("1:%d\n",l->data);
while(L!=NULL){
printf("%d\n",L->data);
L=L->next;
}
printf("------------\n");
L1=List_HeadInsert(L1);
while(L1!=NULL){
printf("%d\n",L1->data);
L1=L1->next;
}
printf("------------\n");
L2=List_TailInsert(L2);
while(L2!=NULL){
printf("%d\n",L2->data);
L2=L2->next;
}
}
单链表(带头结点)
key:
- 删除最后一个结点时会发生什么?为什么?如何解决?
- 头插法、尾插法
- 为什么在循环输入前,需要先输入一次?
代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode{
int data;
struct LNode *next;
}LNode, *LinkList;
//初始化一个带头结点的单链表
bool initLinkList(LinkList &L){
L= (LNode *) malloc(sizeof(LNode));
if(L==NULL){ //内存没有被分配,初始化失败
return false;
}
L->next=NULL;
return true;
}
//按位序插入
bool insertNodeByPos(LinkList &L,int pos,int val){
if(pos<=0){
return false;
}
int i=0;
LNode *p=L;
while(p!=NULL && i<pos-1){ //找到插入位置pos结点的前驱结点
i++;
p=p->next;
}
if(p==NULL){
return false;
}
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=val;
s->next=p->next;
p->next=s;
return true;
}
//插入前驱节点
bool insertPreNode(LNode *node,int val){
if(node==NULL){
return false;
}
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=node->data;
s->next=node->next;
node->data=val;
node->next=s;
}
//插入后继结点
bool insertSucNode(LNode *node,int val){
if(node==NULL){
return false;
}
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=val;
s->next=node->next;
node->next=s;
}
//按位序删除结点
bool delNodeByPos(LinkList &L,int pos){
if(pos<=0){
return false;
}
int i=0;
LNode *p=L;
while(p!=NULL && i<pos-1){
i++;
p=p->next;
}
LNode *s=p->next;
if(s==NULL){
return false;
}
p->next=s->next;
free(s);
}
//删除指点结点
bool delNode(LNode *node){
if(node==NULL){
return false;
}
LNode *p=node->next;
if(p==NULL){ //如何删除最后一个结点 (好像挺复杂的,先不管)
return true;
}
node->data=p->data; //把要删的点与其后继结点交换,
node->next=p->next;
free(p); //删除交换后node的后继结点
}
//按位序查找元素
LNode * getNodeByPos(LinkList L,int pos){
if(pos<=0){
return NULL;
}
int i=1;
LNode *p=L->next; //指向第一个元素
while(p!=NULL && i<pos){
i++;
p=p->next;
}
return p;
}
//按值查找
LNode * LocateNode(LinkList L,int val){
LNode *p=L->next; //跳过头结点
while(p!=NULL && p->data!=val){
p=p->next;
}
return p;
}
//尾插法建立单链表
LinkList List_TailInsert(LinkList &L){
int val;
L=(LinkList)malloc(sizeof(LNode)); //建立头结点
LNode *s,*r=L; //建立尾指针r
scanf("%d",&val); //要先输入一次,否则99999会被输入进去
while(val!=99999){
s=(LNode *)malloc(sizeof(LNode));
s->data=val;
r->next=s;
r=s;
scanf("%d",&val);
}
r->next=NULL; //尾指针置空
return L;
}
//头插法建立单链表
LinkList List_HeadInsert(LinkList &L){
int val;
L=(LinkList) malloc(sizeof(LNode)); //声明头指针
LNode *s,*p=L;
scanf("%d",&val);
p->next=NULL; //头指针后继置空
while(val!=99999){
s=(LNode *)malloc(sizeof(LNode));
s->data=val;
s->next=p->next;
p->next=s;
scanf("%d",&val);
}
return L;
}
int main(){
LinkList L;
LinkList L1;
LinkList L2;
initLinkList (L);
insertNodeByPos(L,1,1);
insertNodeByPos(L,2,2);
insertNodeByPos(L,3,3);
insertNodeByPos(L,4,4);
insertPreNode(L->next,0);
insertSucNode(L->next->next->next->next->next,5);
delNodeByPos(L,6);
delNodeByPos(L,1);
delNode(L->next->next->next->next);
int i=1;
while(i<5){
LNode *p=getNodeByPos(L,i);
printf("%d\n",p->data);
i++;
}
LNode *l=LocateNode(L,4);
printf("4:%d\n",l->data);
//打印尾插法建表
printf("--------------\n");
L1=List_TailInsert(L1);
i=1;
while(i<=5){
LNode *p=getNodeByPos(L1,i);
printf("%d\n",p->data);
i++;
}
//打印头插法建表
printf("--------------\n");
L2=List_HeadInsert(L2);
i=1;
while(i<=5){
LNode *p=getNodeByPos(L2,i);
printf("%d\n",p->data);
i++;
}
return 0;
}
循环单链表
代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//初始化循环单链表
bool initList(LinkList &L){
L= (LNode *)malloc(sizeof(LNode));
if(L==NULL){
return false;
}
L->next=L; //头结点指向自己
return true;
}
//判断循环双链表是否为空
bool isEmpty(LinkList L){
if(L->next==L){
return true;
}else{
return false;
}
}
//判断结点是否为尾结点
bool isTail(LinkList L,LNode *p){
if(p->next==L){
return true;
}else{
return false;
}
}
//在p结点后插入一个结点
bool insertNode(LNode *p,int val){
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=val;
s->next= p->next;
p->next=s;
}
//删除p结点
bool delNode(LNode *p){
//实现这个删除最后一个结点时操作会有点复杂,需要有头结点指针才能完成。
}
//循环单链表
int main(){
LinkList L;
initList(L);
return 0;
}
双链表
代码:
#include<stdio.h>
#include<stdlib.h>
typedef struct DNode{
int data;
struct DNode *prior,*next;
}DNode, *DList;
//初始化双链表
bool initDList(DList &L){
L=(DNode *)malloc(sizeof(DNode)); //声明头结点
L->prior=NULL; //头结点前驱永远为NULL
L->next=NULL; //头结点后继置空
return true;
}
//在结点后插入一个结点
bool insertSucNode(DNode *p,DNode *s){
if(p==NULL){
return false;
}
s->next=p->next;
if(p->next!=NULL){
p->next->prior=s;
}
s->prior=p;
p->next=s;
return true;
}
//删除结点后的一个结点
bool delSucNode(DNode *p){
if(p==NULL){
return false;
}
DNode *q=p->next;
if(q==NULL){
return false;
}
if(q->next!=NULL){
q->next->prior=p;
}
p->next=q->next;
free(q);
return true;
}
//双链表
int main(){
DList dl;
initDList(dl);
for(int i=0;i<5;i++){
DNode *s=(DNode *)malloc(sizeof(DNode));
s->data=i;
insertSucNode(dl,s);
}
delSucNode(dl);
dl=dl->next; //跳过头结点
while(dl!=NULL){
printf("%d\n",dl->data);
dl=dl->next;
}
return 0;
}
链表与顺序表的优劣比较
栈与队列
栈的顺序储存实现
key:
- 一结构体定义为S,为什么不是用S->top 而是用S.top
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 10
typedef struct{
int data[MaxSize];
int top;
}SqStack;
//初始化栈
bool initSqStack(SqStack &S){
S.top=0; //指向栈顶指针后一位元素
return true;
}
//入栈
bool Push(SqStack &S,int x){
if(S.top==MaxSize){ //判断是否栈已满
return false;
}
S.data[S.top]=x;
S.top++;
return true;
}
//出栈
bool Pop(SqStack &S,int &x){
if(S.top==0){ //判断是否栈为空
return false;
}
--S.top;
x=S.data[S.top];
return true;
}
//读取栈顶元素
bool GetTop(SqStack S,int &x){
if(S.top==0){
return false;
}
x=S.data[S.top-1];
return true;
}
//静态数组实现栈
//注意:1.顺序栈的栈顶指针与数据域 分别用S.top、 S.data[]来表示,而不是S->top、S->data[],->在表示指针时才用!
int main() {
SqStack S;
initSqStack(S);
for(int i=0;i<5;i++){
Push(S,i+1);
}
for(int i=0;i<5;i++){
int x;
Pop(S,x);
printf("%d",x);
}
}
链栈
key:
- 注意push操作时,
#include <stdio.h>
#include <stdlib.h>
typedef struct Linknode{
int data;
struct Linknode *next;
} *LinkStack;
bool initLinkStack(LinkStack &S){
S=NULL;
return true;
}
//***重点:这里不太头结点,所以每插入一个元素S就指向它。
bool Push(LinkStack &S,int x){
Linknode *linknode=(Linknode *)malloc(sizeof(Linknode));
linknode->next=S;
linknode->data=x;
S=linknode;
return true;
}
bool Pop(LinkStack &S,int &x){
if(S==NULL){ //判断链栈是否为空
return false;
}
x=S->data;
Linknode *linknode=S->next;
free(S);
S=linknode;
return true;
}
bool GetTop(LinkStack &S,int &x){
if(S==NULL){
return false;
}
x=S->data;
return true;
}
//链栈(不带头结点)
int main(){
LinkStack S;
initLinkStack(S);
for(int i=0;i<5;i++){
Push(S,i+1);
}
for(int i=0;i<5;i++){
int x;
Pop(S,x);
printf("%d",x);
}
}
队列(顺序储存)
key:
- 注意入队与出队时的头指针与尾指针变化
- 判断队列为空/已满的方式有三:
- 空一个位置出来
- 设置size来记录队列个数
- 设置tag来判断队列是空还是满,上次操作为出队tag=0,入队tag=1,因为队空必然上次操作为出队,堆满则为入队,所以tag来判断队列状态。
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 10
typedef struct {
int front;
int rear;
int data[MaxSize];
}SqQueue;
//初始化队列
bool initSqQueue(SqQueue &Q){
Q.front=0;
Q.rear=0;
}
//判断队列是否为空
bool isEmpty(SqQueue Q){
if(Q.front==Q.rear){
return true;
}else{
return false;
}
}
//入队列操作
bool EnQueue(SqQueue &Q,int x){
//判断是否队列已满
if((Q.rear+1)%MaxSize==Q.front){
printf("队列已满\n");
return false;
}
Q.data[Q.rear]= x;
Q.rear=(Q.rear+1)%MaxSize;
return true;
}
//出队操作
bool DeQueue(SqQueue &Q,int &x){
if(isEmpty(Q)){
printf("队列为空\n");
return false;
}
x=Q.data[Q.front];
Q.front=(Q.front+1)%MaxSize;
return true;
}
int main(){
SqQueue Q;
initSqQueue(Q);
for(int i=0;i<9;i++){
EnQueue(Q,i*i);
}
for(int i=0;i<9;i++){
int x;
DeQueue(Q,x);
printf("%d\n",x);
}
return 0;
}
栈(带头结点链式)
#include <stdio.h>
#include <stdlib.h>
typedef struct LinkNode{
int data;
struct LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front,*rear;
}LinkQueue;
//初始化队列
bool initLinkQueue(LinkQueue &Q){
LinkNode *p=(LinkNode *)malloc(sizeof(LinkNode));
Q.front=p;
Q.rear=p;
return true;
}
//判断队列是否为空
bool isEmpty(LinkQueue Q){
if(Q.front==Q.rear){
printf("队列为空");
return true;
}else{
return false;
}
}
//入队操作
bool EnQueue(LinkQueue &Q,int x){
LinkNode *p=(LinkNode *)malloc(sizeof(LinkNode));
p->data=x;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return true;
}
//出队操作
bool DeQueue(LinkQueue &Q,int &x){
if(isEmpty(Q)){
return false;
}
LinkNode *p=Q.front->next;
x=p->data;
Q.front->next=p->next;
if(Q.rear==p){
Q.rear=Q.front;
}
free(p);
return true;
}
int main(){
LinkQueue Q;
initLinkQueue(Q);
for(int i=0;i<10;i++){
EnQueue(Q,i*i);
}
for(int i=0;i<10;i++){
int x;
DeQueue(Q,x);
printf("%d\n",x);
}
return 0;
}
栈(不带头结点链式)
key:
- 初始化队列时,头、尾指针都指向空
- 判空条件:Q.front ==NULL
#include <stdio.h>
#include <stdlib.h>
typedef struct LinkNode{
int data;
struct LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front,*rear;
}LinkQueue;
//初始化队列
bool initLinkQueue(LinkQueue &Q){
Q.front=NULL;
Q.rear=NULL;
return true;
}
//判断是否为空
bool isEmpty(LinkQueue Q){
if(Q.front==NULL){
return true;
}else{
return false;
}
}
//入队操作
bool EnQueue(LinkQueue &Q,int x){
LinkNode *p=(LinkNode *)malloc(sizeof(LinkNode));
p->data=x;
if(isEmpty(Q)){
Q.front=p;
Q.rear=p;
}else{
Q.rear->next=p;
Q.rear=p;
}
return true;
}
//出队操作
bool DeQueue(LinkQueue &Q,int &x){
if(isEmpty(Q)){
return false;
}
LinkNode *p=Q.front;
x=p->data;
if(Q.front==Q.rear){ //判断队列是否只能一个元素
Q.front=NULL; //头、尾指针指向空
Q.rear=NULL;
}else{
Q.front=p->next;
}
free(p);
}
int main(){
LinkQueue Q;
initLinkQueue(Q);
for(int i=0;i<10;i++){
EnQueue(Q,i*i);
}
for(int i=0;i<10;i++){
int x;
DeQueue(Q,x);
printf("%d\n",x);
}
return 0;
}