数据结构之线性表

文字模块

代码模块

一.顺序表

1.定义

typedef int Datatype:  //Datatype可为任何类型,在此为int

#define MAXSIZE 1024 //线性表可能的最大长度,假设为1024

typedef struct{
  Datatype data[MAXSIZE];
  int last;
}SequenList;

2.基本运算

1.建立顺序表L

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>

#define MAXSIZE 1024
typedef char Datatype;

typedef struct{
Datatype data[MAXSIZE];
int last; //指示线性表终端节点的位置

}SequenLIst;

//建立顺序表L;
SequenList *L;
SequenList *Create(){
  int i=0; char ch;
  L=(SequenList*)malloc(sizeof(SequenList)); //分配顺序表空间
  L->last=-1;
  printf("请输入顺序表L中的元素,以字符'#'结束.\n");
  while((ch=getche())!='#'){
  L->data[i++]=ch;
  L->last++;
}
 return L;
}

2.插入数据

将新节点x插入顺序表L的第i个位置上
int Insert(SequenList *l,char x,int i){
  //考虑到表空间溢出,非法位置两问题
  int j;
  if(L->last>=MAXSIZE-1) {
   printf("overflow\n");
   return 0;
}
   else if((i<1)||(i>(L->last+2))){
  printf("errror\n");
  retrurn 0;
}
 else{
  for(j=L->last;j>=i-1;j--){
     L->data[j+1]=L->data[j]; //结点后移
}
  L->data[i-1]=x;
  L->last+=1;
}
return 1;
}

3. 输出顺序表

void Output(SequenList *L){
 printf("\n 顺序表L中的元素为:");
 for(int i=0;i<=L->last;i++){
 printf("%c",L->data[i]);
}
}

4.主程序

int main(){
 char ch;
 int i,ret;
 L=Create();
 scanf("%c",&ch);
 scanf("%d",&i);
 ret=Insert(L,ch,i);
 if(ret){
 Output(L);
 
}
return 0;
}

二、链表

链表的每个节点采用结构体的形式组织:

typedef struct student{
  int num;
  char name[20];
  struct student *next;
}STU;

链表节点分为两个域

      数据域:存放各种实际的数据,如:num、score等

      指针域:存放下一节点的首地址,如:next等. 因此链表的存储密度小于顺序表

链表的操作:
      链表最大的作用是通过节点把离散的数据链接在一起,组成一个表,这大概就是链表 的字面解释了吧。 链表常规的操作就是节点的插入和删除,为了顺利的插入,通常一条链 表我们会人为地规定一个根节点,这个根节点称为生产者。通常根节点还会有一个节点计 数器,用于统计整条链表的节点个数,具体见图3中的 root_node。

双向链表

      双向链表与单向链表的区别就是节点中有两个节点指针,分别指向前后两个节点,其 它完全一样。有关双向链表的文字描述参考单向链表小节即可,有关双向链表的示意图具体见图4

以双向链表为例, 链表是通过节点把离散的数据链接成一个表,通过对节点的插入和删除操作从而实现 对数据的存取。而数组是通过开辟一段连续的内存来存储数据,这是数组和链表最大的区 别。数组的每个成员对应链表的节点,成员和节点的数据类型可以是标准的 C 类型或者是 用户自定义的结构体。数组有起始地址和结束地址,而链表是一个圈,没有头和尾之分, 但是为了方便节点的插入和删除操作会人为的规定一个根节点。

双向链表结构:

typedef char Datatype;
typedef struct node{
  Datatype data;
  struct dnode *prior,*next;
}Dlinklist;
Dlinklist *head;

删除节点:

void DeleteNode(DlinkLIst *p){
 p->prior->next=p->next;
 p->next->prior=p->prior;
 free(p);
}

插入节点:

// 在节点p前插入新节点
void InsertBefore(DlinkList *p,datatype x){
  DlinkList *s;
  s=(DlinkList *)malloc(sizeof(DlinkList));
  s->data=x;
  s->prior=p->prior;
  s->next=p;
  p->prior->next=s;
  p->prior=s;
}

操作

① 创建链表

#include<stdio.h>
#include<stdlib.h>

//定义结点结构体
typedef struct student{
// 数据域
int num;  //学号
int score;  //分数
char name[20]; //姓名
// 指针域
struct student *next;
}STU;

// 创建头节点
 void link_creat_head(STU **p_head,STU *p_new)
{
  STU *p_mov=*p_head;   //*p_head是头节点
  if(*p_head==NULL){
     //对头结点进行初始化
     *p_head=p_new;
      p_new->next=NULL;
}
 else{
  while(p_mov->next!=NULL){
     p_mov=p_mov->next;  //找到原有链表最后一个节点
}

 p_mov->next=p_new;
p_new->next=NULL;
}
}

主程序

int main(){
STU *head=NULL,*p_new=NULL;
 int num;
printf("请输入链表初始个数\n");
scanf("%d",&num);
for(int i=0;i<num;i++){
 p_new=(STU*)malloc(sizeof(STU)); 申请一个新节点
 printf("请输入学号,分数,名字: \n"); 给新结点赋值
 scanf("%d %d %s",&p_new->num,&p_new->score,p_new->name);
 link_creat_head(&head,p_new); //将新节点加入链表
}

return 0;
}

②链表的遍历

第一步:输出第一个节点的数据域,输出完毕后,让指针保存后一个节点的地址

 第二步:输出移动地址对应的节点的数据域,输出完毕后,指针继续后移 ,直到节点指针域为NULL

void link_print(STU *head){
 STU *p_mov;
 p_mov=head;
 while(p_mov->next!=NULL){
   printf("num=%d score=%d name:%s\n",p_mov->num,p_mov->score,p_mov->name);
   p_mov=p_mov->next;
}
}

③链表的释放

重新定义一个指针q,保存p指向节点的地址,然后p后移保存下一个节点的地址,然后释放q对应的节点,以此类推,直到p为NULL为止

void link_free(STU **p_head){
 STU *pb=*p_head;
 while(p_head!=NULL){
  pb=*p_head;
  free(pb);
  pb=NULL;
}
}

④链表节点的查找

先对比第一个结点的数据域是否是想要的数据,如果是就直接返回,如果不是则继续查找下 一个结点,如果到达最后一个结点的时候都没有匹配的数据,说明要查找数据不存在

//按照学号查找

STU * link_search_num(STU *head,int num){
 STU *p_mov;
 p_mov=head;
 while(p_mov!=NULL){
  if(p_mov==num){
   return p_mov;
}
p_mov=p_mov->next;
}
return NULL;
}


//按照姓名查找
STU * link_search_name(STU *head,char *name){
 STU *p_mov;
 p_mov=head;
 while(p_mov!=NULL){
 if(strcmp(p_mov->name,name)==0){
  return p_mov;
}
p_mov=p_mov->next;
}
 return NULL;
}

⑤链表节点的删除

 如果链表为空,不需要删除 如果删除的是第一个结点,则需要将保存链表首地址的指针保存第一个结点的下一个结点的 地址 如果删除的是中间结点,则找到中间结点的前一个结点,让前一个结点的指针域保存这个结 点的后一个结点的地址即可

void link_delete_num(STU **p_head,int num){
  STU *pb,*pf;
  pb=pf-*p_head;
  if(*p_head==NULL){
  return;
}
 while(pb->num!=num && pb->next!=NULL){
  pf=pb;
  pb=pb->next;
}
 if(pb->num==num){
  if(pb==*p_head){
    *p_head=pb->next;
}
 else{
    pf->next=pb->next;
  }

 free(pb);
 pb=NULL;
}
else{
 printf("没有您要删除的节点\n");
}
}

⑥链表中插入一个节点

情况(按照从小到大):

      如果链表没有结点,则新插入的就是第一个结点。

      如果新插入的结点的数值最小,则作为头结点。

      如果新插入的结点的数值在中间位置,则找到前一个,然后插入到他们中间。

      如果新插入的结点的数值最大,则插入到最后。
 

  • 41
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值