No.2.4 链表


一、链表简介

数组/列表:作为一个整体出现,只能在头、尾进行更新操作

链表:指针+动态分配内存函数malloc实现,每个元素都有一个后向指针,指明下一个元素的位置,尾元素有个空指针。

1.关于指针:

  int a=10, *p; // * 间接运算符,作用是取得指针 p 指向的内存地址保存的变量值

  p = &a;   // &为取地址符,这样 p 就获得了 a 的内存地址

  *p = 100;     // a == 100

2.关于动态分配内存函数 malloc :返回一个 void * 未确定类型的指针,它可以强制转换为任何其他类型的指针。

  int *p;

  p = (int *)malloc(sizeof(int));  //指针 p 获取动态分配的内存空间地址

注意:指针指向的是内存空间的首地址(第一个字节的地址),而指针类型决定了作为一个整体的内存长度;

 3.链表结构体

struct node

  {  int data;  //存储具体数据

     struct node *next;  //指针,存储下一个节点的地址,因为下一个节点类型也是struct node,所以该指针也必须是struct node类型的指针

struct node *head;  //链表首先要有一个头指针

head = NULL;     //初始化时,头指针为空

4.代码练习

EG1. 向链表中写入数据

int main(){
  struct node *head,*q,*p,*t;
  int i,b,nn;
  scanf("%d",&nn);

  head = NULL; //头指针为空
  for (i=1;i<=nn;i++){ //循环读取n个数
    scanf("%d", &b);

  //动态申请一个空间以存放node,并用临时指针p指向这个节点
  p = (struct node *)malloc(sizeof(struct node));  //释放空间 free
  p->data = b;
  p->next=NULL; //尾指针为空
  if(head==NULL)
    head=p; //如果这是创建的第一个节点,则将头指针指向该节点
  else
    q->next = p; //若不是第一个创建的节点,则将上一个节点的后续指针指向该节点
  q=p; //递归,q 重置为尾节点
  }

  t = head;
  while (t!=NULL){
    printf("%d ",t->data);
    t=t->next; //递归,t 重置为下一个节点
  }
return 0;
}

EG2.向链表中插入数据

int main(){
  struct node *head,*q,*p,*t,*y;
  int i,b,nn;
  printf("请输入链表长度:\n");
  scanf("%d",&nn);

  head = NULL; //头指针为NULL
  printf("请输入各节点整数值:\n");
  for (i=1;i<=nn;i++){ //循环读取n个数
  scanf("%d", &b);
  //动态申请一个空间以存放node,并用临时指针p指向这个节点
  p = (struct node *)malloc(sizeof(struct node));
  p->data = b;
  p->next=NULL; //尾指针为空
  if(head==NULL)
    head=p; //如果这是创建的第一个节点,则将头指针指向该节点
  else
    q->next = p; //若不是第一个创建的节点,则将上一个节点的后续指针指向该节点
  q=p; //递归,q 重置为尾节点
  }

  printf("当前链表为:\n");
  t=head;
  while (t!=NULL){
    printf("%d ",t->data);
    t=t->next; //递归,t 重置为下一个节点
  }

  printf("请输入要插入链表的值:\n");
  scanf("%d",&b);
  t = head;
  while (t->data <=b){
  t=t->next;
  }
  y=t->next;
  t->data=b;
  t->next=y;

  printf("新链表值为:\n");
  t=head;
  while (t!=NULL){
    printf("%d ",t->data);
    t=t->next; //递归,t 重置为下一个节点
  }
  getchar();
  return 0;
}

EG2.测试会发现,插入的值把后面一个节点替代了,而不是要求的那样“新增一个节点,后面的节点全部后移“。正确的做法应该是这样:

scanf("%d",&a);//读入待插入的数
t=head;//从链表头部开始遍历
while(t!=NULL)//当没有到达链表尾部的时候循环
{
  if(t->next->data > a)  //如果当前结点下一个结点的值大于待插入数,将数插入到中间
  {
    p=(struct node *)malloc(sizeof(struct node));//动态申请一个空间,用来存放新增结点
    p->data=a;
    p->next=t->next;  //新增结点的后继指针指向当前结点的后继指针所指向的结点
    t->next=p;     //当前结点的后继指针指向新增结点
    break;         //插入完毕退出循环
  }
  t=t->next;//继续下一个结点
}

5.讨论:还是老问题,弄清楚head到底是节点自身,还是指向节点的指针!

上述代码是有点问题的:

  a.head直接指向了头节点,而判断语句if(t->next->data > a),直接跳过了头结点进行判断,导致头查有问题:

    要么head 作为一个空节点独立存在,

    要么以 head->data作为判断起点
  b.这时又出现另外一个问题,比如向(1,3,5)中插入2,指针已经指到了3,那么就要交换节点的值,然后进行指针操作;

  c.尾节点的插入相似。

相关