线性表之离散存储(链表)

来源:互联网 发布:信广立诚贷之家数据 编辑:程序博客网 时间:2024/05/20 06:52

1.什么是链表?

  链表和数组都是线性表的分支.逻辑上他们都是相连的。但是在物理上面链表和数组相反,链表具有以下四个性质:

    (1)n个节点离散分配
    (2)节点彼此通过指针相连
    (3)每一个节点只有一个前驱节点和一个后驱节点
    (4)首节点没有前驱节点 尾节点没有后续节点


2.链表的专业术语?

  首节点:第一个有效节点(要有数据域和指针域)
  尾节点:最后一个有效节点
  头结点:第一个有效节点之前的那个节点
      头结点并不存放有效数据(数据域)
      它只有指针域,指向首节点
      加头结点就是为了方便对链表的操作
  头指针:指向头结点的指针变量
  尾指针:指向尾节点的指针变量

  注意:和数组一样,我们确定一个数组只需要知道首节点的地址,数组的有效长度,数据最大长度.那么确定一个链表需要几个参数呢?
    答:只需要一个参数,头指针,知道头指针可以很快知道下面各个节点的信息

3.链表的分类
  链表可以分为四类:
    单链表:只有一个指针域,它是指向下一个节点
    双链表:一个节点有两个指针域,分别指向前后两个节点元素
    循环链表:节点与节点之间形成一个环状,可以找到任意一个节点
    非循环链表:就是普通的链表(可双可单)


4.链表的算法操作

  PNODE createNodeList(int len);//初始化链表(给链表分配len个长度的内存空间)
  void showNodeList(PNODE pHead);//显示链表的基本信息
  bool is_empty(PNODE pHead);//判断链表是否为空
  int NodeList_Length(PNODE pHead);//获得链表的长度
  bool insert_NodeList(PNODE pHead,int pos,int val);//在链表指定位置(pos>=1)插入值(val)
  bool delete_NodeList(PNODE pHead,int pos);//删除指定位置的数值
  bool update_NodeList(PNODE pHead,int pos,int val);//更改指定位置的数值为val
  NODE get(PNODE pHead,int pos);//获得指定位置的数值
  void sort_NodeList(PNODE pHead);//升序排序

  1)初始化链表,先要动态分配len个长度的内存空间给头指针(pHead),然后判断空间是否分配正确,如果正确的话,那么这个头指针也代表尾指针(因为开始只有一个节点)。接着每循环一次创建一个新的节点(pNew),将其挂在尾指针(pTail)后面,如此一来,链表初始化完毕.

//初始化链表PNODE createNodeList(int len){PNODE pTail;int i,val; PNODE pHead = (PNODE)malloc(sizeof(NODE)*len);//动态分类len个长度空间if(pHead==NULL){printf("链表空间分配失败,退出程序!");exit(-1); }pTail = pHead;pTail->pNext=NULL;for(i=0;i<len;i++){PNODE pNew = (PNODE)malloc(sizeof(NODE));printf("请输入第%d个元素的值:",i+1);scanf("%d",&val);pNew->data=val;pTail->pNext=pNew;pNew->pNext=NULL;pTail=pNew;}return pHead;}
2)输出链表的基本信息,是通过while循环来判断的,只要是p->pNext不为空,那么就说明整个链表有节点存在,便输出p->data.

//输出链表的值void showNodeList(PNODE pHead){PNODE p = pHead->pNext;int num = NodeList_Length(pHead);if(num!=0){printf("链表的长度为%d,分别为",num);while(p!=NULL){printf("%d ",p->data);p=p->pNext;}printf("\n");}} 
3)判断链表是否为空,比较简单,只需要判断头指针pHead->pNext是否为空,也就是头指针后面是否有节点就可以了.

//判断链表为不为空bool is_empty(PNODE pHead){PNODE p = pHead->pNext;if(p==NULL)return true;return false;}
4)获得链表的长度,先定义一个中间变量num,然后通过while循环判断p->pNext是否为空,不为空就加一,直到不满足循环条件就说明链表已经到头了,长度因此获得.

int NodeList_Length(PNODE pHead){int num=0;PNODE p = pHead->pNext;if(is_empty(pHead))return 0;while(p!=NULL){num++;p=p->pNext;}return num;}
5)往链表指定位置插入一个数值,这个算法相对而言比较麻烦。因为链表不像数组那样,可以很快的定位插入的位置,所以链表的插入算法首先要找到插入位置的前一个节点,然后自己要新建一个pNew新节点用来存放想要插入的值,接着将pNew与前一个节点相互连接,pNew与插入位置后一个节点再连接一下,表示插入成功.基本思想就是这样.

//往链表中插入一个元素 bool insert_NodeList(PNODE pHead,int pos,int val){PNODE p = pHead->pNext;PNODE pNew,pTemp;int i=1;if(pos<1||pos>NodeList_Length(pHead)+1)return false;//1 2 34 4 5 6 7    :pos=6while(p!=NULL&&i<pos-1){i++;p=p->pNext;//p已经是第五个节点了 }pNew=(PNODE)malloc(sizeof(NODE));pTemp = p->pNext;pNew->data=val;p->pNext=pNew;pNew->pNext=pTemp;return true;}
6)链表的删除节点算法

bool delete_NodeList(PNODE pHead,int pos){int i=1;PNODE pTemp;PNODE p = pHead->pNext;if(p==NULL){printf("链表为空,无法继续删除,退出程序!");return false;}// 1 2 3 4 5 6  pos=3;while(p!=NULL&&i<pos-1){i++;p=p->pNext;  //此时的p是要删除的数的前一个 }pTemp = p->pNext;p->pNext = p->pNext->pNext;free(pTemp);return true;}
7)链表的更改节点算法

bool update_NodeList(PNODE pHead,int pos,int val){int i=1;PNODE p =pHead->pNext;if(p==NULL){printf("链表为空,无法修改,退出程序!");return false;}while(p!=NULL&&i<pos){i++;p=p->pNext;}p->data=val;return true;}
8)获取指定位置的数据(返回节点元素)

NODE get(PNODE pHead,int pos){PNODE p = pHead->pNext;int i=1;while(p!=NULL&&i<pos){i++;p=p->pNext;}return *p;}
9)链表排序算法

void sort_NodeList(PNODE pHead){PNODE p,q;int i,j;int length= NodeList_Length(pHead);for(i=0,p=pHead->pNext;i<length-1;i++,p=p->pNext){for(j=i+1,q=p->pNext;j<length;j++,q=q->pNext){if(p->data>q->data){int temp=p->data;p->data=q->data;q->data=temp;}}}}


总结:个人感觉链表算法还是很重要的,因为后面的队列,栈,树都要用到链表,所以链表既是重点又是难点.(这几天一直很忙,更新有点慢,下一篇就是栈和队列)

链表源码下载地址:http://download.csdn.net/download/qq_31308883/10148594