说明:
以下代码均用c++编写,
linux下可以编译和执行:
g++ LinkList_use.cpp -o LinkList_use
./LinkList_use
易错点:
1.链表的表头指针和表头结点
平时我们看到的定义是这样的:
typedef struct LNode{
int data;
struct LNode *next;
}LNode, *LinkList;
这里的LinkList
加了*
后,总是让人很迷惑,
再加上一些什么头结点,整个人都晕晕的了……
实际上,所有的LinkList
都指文中的红色部分,它只是一个指针,占用内存为8字节!
而头结点
是绿色区域,不仅需要头指针,还需要一个额外的空结点,它的data是空,指针指向第一个数值。
2.链表在函数内部的引用:
可能你说上面太简单了,那么,看下面的函数:
这里的LinkList L
应该是LinkList &L
还是LinkList *L
还是LinkList L
呢?
/**
* 递归算法
* 删除不带头结点的单链表里的所有值为x的结点
*
* 终止条件:子链表为空
* 递归主体:如果发现了值为x的结点,删除L结点,
* 否则继续去子链里找
*
*/
void DeleteAllX(LinkList ?L,int x){
if(L!=NULL)
if(x!=L->data)
DeleteAllX(L->next,x);
else
{
LNode *p=L;
L = L->next;
free(p);
DeleteAllX(L,x);
}
else
return;
}
正确答案是LinkList &L
,
首先,调用函数的时候:
LinkList l1;
l1=List_HeadInsert(l1);//创建结点
PrintList(l1);
DeleteAllX(l1,2); //递归删除结点中为data==2的结点
PrintList(l1);
LinkList L
传入了一个头指针,它存储了指针,得到的结果会是一堆奇怪的值,因为虽然指针被传递过去了,但只是一个copy的值,一旦函数结束,
LinkList *L
肯定不行,编译会直接报错,因为传参是DeleteAllX(l1,2)
;除非是DeleteAllX(&l1,2);
这样子传过去,但要使用L->data
也是会报错的
应该是LinkList &L
,这样获得的就是指针本身,对L进行各种修改也是会被更新的
可以看到如果没有使用&L
,L = L->next;
永远也不能生效~!!
3.其他类型的链表~
/****
*
* 双链表
*
*/
typedef struct DNode{
int data;
struct DNode *prior,*next;
}DNode, *DLinkList;
/**
* 静态链表
* 表头 SLinkList[index] 其中表头为index=0 ;
* 表尾 next = -1;
*/
# define MaxSize 50
typedef struct{
int data;
int next;
}SLinkList[MaxSize];
代码:
LinkList.h:
typedef struct LNode{
int data;
struct LNode *next;
}LNode, *LinkList;
/***
* 利用头插法建立单链表
*/
LinkList List_HeadInsert(LinkList &L){
std::cout<<"开始用头插法创建链表,输入-1结束"<<std::endl;
LNode *s;
int x;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
scanf("%d",&x);
while(x != -1){
s = (LNode*)malloc(sizeof(LNode)); //创建结点
s->data = x;
s->next = L-> next;
L->next = s;
scanf("%d",&x);
}
return L;
}
/***
* 利用尾插法建立单链表
*/
LinkList List_TailInsert(LinkList &L){
std::cout<<"开始用尾插法创建链表,输入-1结束"<<std::endl;
LNode *s;
LNode *insertP;
int x;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
insertP = L; //保存最后一个结点的指针
scanf("%d",&x);
while(x !=-1){
s = (LNode*)malloc(sizeof(LNode)); //创建结点
s->data = x;
insertP->next = s;
insertP = s;
scanf("%d",&x);
}
insertP->next=NULL;
return L;
}
/**
* 打印链表
*/
void PrintList(LinkList &L){
LNode *s=L->next;
while(s!=NULL){
std::cout<<s->data<<" "<<s<<std::endl;
s=s->next;
}
std::cout<<std::endl;
}
/**
* 按照序号查找结点,返回该结点指针
* 如果超出了结点范围,返回NULL;
*/
LNode* FindIndexNode(LinkList L,int i){
LNode* p = L;
int j=0;
if(i==0)
return L;
if(i<0)
return NULL;
while(p != NULL && i<j){ //循环的习惯:遇到了可以跳出去的条件,不要犹豫,立刻跳出去!!
if(i==j)
return p;
else{
p = p->next;
j++;
}
}
return NULL;
}
/**
* 按照值进行遍历查找,只返回第一个找到的x结点
*/
LNode* FindNumNode(LinkList L, int x){
LNode *p = L;
while(p!=NULL){
if(x==p->data)
return p;
p=p->next;
}
return NULL;
}
/****
*
* 双链表
*
*/
typedef struct DNode{
int data;
struct DNode *prior,*next;
}DNode, *DLinkList;
/**
* 静态链表
* 表头 SLinkList[index] 0 ;
* 表尾 next = -1;
*/
# define MaxSize 50
typedef struct{
int data;
int next;
}SLinkList[MaxSize];
/**
* 递归算法
* 删除不带头结点的单链表里的所有值为x的结点
*
* 终止条件:子链表为空
* 递归主体:如果发现了值为x的结点,删除L结点,
* 否则继续去子链里找
*
*/
void DeleteAllX(LinkList L,int x){
//std::cout<<L<<" "<<" "<<std::endl;
if(L!=NULL)
if(x!=L->data)
DeleteAllX(L->next,x);
else
{
LNode *p=L;
L = L->next;
free(p);
DeleteAllX(L,x);
}
else
return;
}
LinkList_use:
#include<iostream>
#include"LinkList.h"
#include<malloc.h>
int main(){
LinkList l1,l2;
l1=List_HeadInsert(l1);
PrintList(l1);
//l2=List_TailInsert(l2);
//PrintList(l2);
std::cout<<" "<<l1<<" "<<&l1<<std::endl;
DeleteAllX(l1,2);
std::cout<<" "<<l1<<" "<<&l1<<std::endl;
PrintList(l1);
}