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=6
while(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.youkuaiyun.com/download/qq_31308883/10148594