数据结构之链表

数据结构 — 链表


前言

此部分为单链表,未做图例说明讲解,仅为代码理解部分。后续有时间补上详尽

一、定义链表

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
// #include <stdbool.h>
typedef int ElemType;     // 自定义链表的数据元素为整数。

typedef enum { 
  false = 0, 
  true 
  }bool;

/*定义链表*/
typedef struct LNode{
  ElemType data;          //数据域
  struct LNode *next;     //指针域
}LNode,*LinkList;
//LNode     等价于 struct LNode   (结点)
//LinkList  等价于 struct LNode * (链表)

二、创建链表:createList_pHead

/*创建链表*/
LinkList createList_pHead(void)
{
  int len = 0;  //用来存放有效结点的个数
  int i = 0;
  int val = 0;  //用来临时存放用户输入结点的值

  //分配一个不存放有效数据的头结点
  LinkList pHead = (LinkList)malloc(sizeof(LNode));
  if(pHead == NULL) {
    printf("分配失败,终止程序!\n");
    exit(-1);
    // return NULL; 
  }

  /*定义pTail始终指向尾结点,开始时指向头结点*/
  LinkList pTail = pHead;
  pTail->next = NULL;

  printf("输入需要生成的链表结点个数:len = ");
  scanf("%d",&len);

  for(i = 0; i < len ; i++)
  {
    printf("输入第%d个结点的值:",i+1);
    scanf("%d",&val);

    /*申请新结点*/
    LinkList pNew = (LinkList)malloc(sizeof(LNode));
    if(NULL == pNew){
      printf("分配失败,终止程序!\n");
      exit(-1);
    }
    pNew->data = val;         /*将值赋值给新结点的数据域*/
    pTail->next = pNew;       /*pNew(地址)赋值给pTail的指针域,尾结点指向新结点*/
    pNew->next = NULL;        /*pNew清空*/
    pTail = pNew;             /*pNew赋值给pTail,更新尾部指针。(pNew作为链表的尾指针,为下次指向新结点做准备)*/
  }

  return pHead;
}

三、遍历链表:traverseList_OUT

/*遍历链表输出*/
bool traverseList_OUT(LinkList pHead)
{
  LinkList p = pHead->next;  /*p开始指向第一个有效结点 (pHead->next)*/
  /*当p不为空,就一直输出数值*/
  while (NULL != p)
  {
    printf("%d ",p->data);
    p = p->next;/*指向下一个结点,相当于p++,但是链表不是连续的,所以不能使用地址++*/
  }
  printf("\n");
  return true;
}

四、链表判空:isEmpty

/*判断链表是否为空,返回值:false-非空或失败,true-空。*/
bool isEmpty(LinkList pHead)
{
  if(NULL == pHead->next)
    return true;
  else
    return false;
}

不为空测试
在这里插入图片描述

为空测试
在这里插入图片描述

五、获取链表长度:getLengthList

/* 求链表的长度,返回值:>=0-表pHead结点的个数。*/
int  getLengthList(LinkList pHead)
{
  LNode *pp=pHead->next;  // 头结点不算,从第1个结点开始。

  int length=0;

  while (pp != NULL) 
  { 
    pp=pp->next; 
    length++;
  }

  return length;
}

测试:
在这里插入图片描述

六、链表排序:sortList

/*链表排序*/
void sortList(LinkList pHead)
{
#if 0  
/*
//数组类型排序,作为参考  
  int i,j,t;
  for(i = 0 ; i < len-1; i++)
    for(j = i+1; j < len; j++)
    {
      if(a[i] > a[j])
      {
        t = a[i];
        a[i] = a[j];
        a[j] = t;
      }
    }
*/    
  int i,j,t;
  int len = getLengthList(pHead);
  LinkList p,q;
  for(i = 0,p = pHead->next; i < len - 1; i++,p = p->next)
  {
    for(j = i+1,q = p->next; j < len; j++,q = q->next)
    {
      if(p->data > q->data)   //类似于数组中的: a[i] > a[j]
      {
        t = p->data;          //类似数组中的:t = a[i]
        p->data = q->data;    //类似于数组中的: a[i] = a[j]
        q->data = t;          //类似于数组中的: a[j] = t
      }
    }
  }

#else

/*
//冒泡排序
  int i,j,t;
  int len = getLengthList(pHead);
  for(i = 0; i < len-1; i++)
    for(j = 0; j < len-1-i; j++)
    {
      if(a[j] > a[j+1])
      {
        t = a[j];
        a[j] = a[j+1];
        a[j+1] = t;
      }
    }
*/
  int i,j,t;
  int len = getLengthList(pHead);
  LinkList p,q;
  for(i = 0,p = pHead->next; i < len-1; i++,p = p->next)
    for(j = 0,q = pHead->next; j < len-1-i; j++,q = q->next)
    {
      if(q->data > q->next->data)
      {
        t = q->data;
        q->data = q->next->data;
        q->next->data = t;
      }
    }
#endif

}

测试:
在这里插入图片描述

七、链表插入:insertList

/*链表插入 */
//在链表指定位置插入给定的值
//在链表的第 i 个位置之前插入元素 e
int insertList(LinkList pHead, int pos, int val)
{
  if(pos < 1) return -1; //不合法
  int i = 0;
  LNode *p = pHead;     //pHead为头指针,p为头结点
  /*寻找第i个结点*/
  while( p!=NULL && i < pos-1)
  {
    p = p->next;
    ++i;
  }

  /*第i个结点不存在*/
  if(i > pos-1 || NULL == p) 
    return false;

  /*生成新结点*/
  LNode *pNew = (LNode *)malloc(sizeof(LNode));
  if(pNew == NULL)
  {
    printf("动态分配内存失败!\n");
    exit(-1);
  }

  pNew->data = val;
  pNew->next = p->next;  /*将p的下一个结点地址赋值给pNew的指针域,即pNew->next指向p->next*/
  p->next = pNew;        /*将pNew地址赋值给p的指针域,即p->next指向pNew*/
  return true;
}

在这里插入图片描述

八、删除链表元素:deleteList

/*链表删除 */
//在链表指定位置删除给定的值
//在链表的第 i 个位置之前插入元素 e
int deleteList(LinkList pHead, int pos, int *pVal)
{

  if(pos < 1) return -1; //不合法

  int i = 0;
  LNode *p = pHead;     //pHead为头指针,p为头结点
  /*寻找第i个结点*/
  while( p->next!=NULL && i < pos-1)
  {
    p = p->next;
    ++i;
  }

  /*第i个结点不存在*/
  if(i > pos-1 || NULL == p->next) 
    return false;

  LinkList q = p->next;	//不加这个,容易造成内存泄漏
  *pVal = q->data;
  p->next = p->next->next;
  free(q);
  return true;  
}

在这里插入图片描述

九、清空链表:ClearList

// 清空链表。
void ClearList(LinkList pHead)
{
  // 清空链表LL是指释放链表全部的结点,但不包括头结点。
  if (pHead == NULL) { printf("链表LL不存在。\n"); return; } // 判断链表是否存在。

  LNode *tmp1;
  LNode *tmp2=pHead->next;  // 保留头结点,从头结点的下一个结点开始释放。

  while(tmp2!=NULL)
  {
    tmp1=tmp2->next;
    free(tmp2);
    tmp2=tmp1;
  }

  pHead->next=NULL; // 这行代码一定不能少,否则会留下野指针。

  return;
}

测试:
在这里插入图片描述

十、链表元素反转:reverseList

// 把链表pp结点之后的结点原地逆置(反转),返回值:0-失败;1-成功。
void reverseList(LNode *pp)
{
  LNode *ss;      // 当前结点。
  LNode *ssnext;  // 当前结点的下一结点。

  ss=pp->next;    // 从pp结点之后的结点开始反转。

  pp->next=NULL;  // pp->next指向空。

  while (ss != NULL)
  {
    ssnext=ss->next;  // 保留ss下一结点的地址。

    // 以下两行相当于在pp之后插入ss结点。
    ss->next=pp->next;  
    pp->next=ss;

    ss=ssnext;  // ss结点后移。
  }
}

在这里插入图片描述

// 采用归并的方法,将两个升序的链表La和Lb,合并成一个升序的链表Lc。
int mergeList(LinkList La,LinkList Lb,LinkList Lc)
{
  if ( (La == NULL) || (Lb == NULL) || (Lc == NULL) ) { printf("表La、Lb、Lc至少有一个不存在。\n"); return 0; }

  La=La->next;
  Lb=Lb->next;

  LNode *pp;

  // 把La和Lb合并到Lc中。
  while ( (La != NULL) && (Lb != NULL) )
  {
    // 取La和Lb的较小者。
    if (La->data <= Lb->data)
    {
      pp=La; La=La->next;
    }
    else
    {
      pp=Lb; Lb=Lb->next;
    }

    // 把较小者追加到Lc中。
    Lc->next=(LNode *)malloc(sizeof(LNode));  // 分配一个新结点。
    Lc=Lc->next;
    memcpy(&Lc->data,&pp->data,sizeof(ElemType));
    Lc->next=NULL;
  }

  // 把链表La其它的元素追加到Lc中。
  while (La != NULL)
  {
    Lc->next=(LNode *)malloc(sizeof(LNode));  // 分配一个新结点。
    Lc=Lc->next;
    memcpy(&Lc->data,&La->data,sizeof(ElemType));
    Lc->next=NULL;
    La=La->next;
  }
    
  // 把链表Lb其它的元素追加到Lc中。
  while (Lb != NULL)
  {
    Lc->next=(LNode *)malloc(sizeof(LNode));  // 分配一个新结点。
    Lc=Lc->next;
    memcpy(&Lc->data,&Lb->data,sizeof(ElemType));
    Lc->next=NULL;
    Lb=Lb->next;
  }

  return 1;
}

十一、实验

int main(void)
{
  int val;
  LinkList pHead = NULL;
  pHead = createList_pHead();
  printf("生成的链表数值为:\n");
  traverseList_OUT(pHead);

  /*插入*/
  printf("在第3个位置插入 22\n");
  if(insertList(pHead,3,22) == 1){
    traverseList_OUT(pHead);
    printf("插入成功\n");
  }else{
    printf("插入失败\n");
  }

  /*删除*/
  printf("删除第4个元素\n");
  if(deleteList(pHead,4,&val)){
    printf("删除成功,你删除的元素是:%d\n",val);
  }else{
    printf("删除失败,你删除的元素是:%d\n",val);
  }
  traverseList_OUT(pHead);
  
  /*排序*/
  printf("生成的链表升序之后为:\n");
  sortList(pHead);
  traverseList_OUT(pHead);
  
  /*链表逆转*/
  printf("生成的链表反转之后为:\n");
  reverseList(pHead);
  traverseList_OUT(pHead);
	
  /*清空链表*/
  printf("清空链表!\n");
  ClearList(pHead);
  traverseList_OUT(pHead);
  
  /*判空*/
  if(isEmpty(pHead)) printf("链表为空!\n");
  else printf("链表不为空\n");

  /*获取长度*/
  int len = getLengthList(pHead);
  printf("链表的长度为:%d",len);

  return 0;
}

总结

待续
2021/10/08
双链表,循环链表……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Terry.Z_1009

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值