定义:由指针把若干个节点链接成链状的结构。
特点:A.链表存储的数据在地址空间上可连续,可不连续。B.链表中的每一个节点都包括数据和指向下一个地址的指针。C.查找数据的时间复杂度为O(n),方便数据的增删。
一、单向链表的C语言实现链表是一种线性存储数据的结构,存储内容在逻辑上连续的,在物理上却不一定连续。单向链表的组成包括一个链表头(head)和若干链表元素(node),对链表的基本操作其实就是增、删、改、查。
首先说说单向链表的C语言实现方法。为了实现一个单向链表,首先定义一个结构体:
//定义一个表示链表的结构体指针
struct list {
int id; // 标识这个元素方便查找
char data[20]; // 链表中包含的元素
struct list *next; // 指向下一个链表的指针
};
下面来编写程序实现一个链表的基本操作。程序的功能是首先分配若干个链表元素(node),然后对这些链表元素进行赋初值,赋完初值之后将这些链表元素依次加入到链表当中,最后把这些元素的data字段依次打印出来。(注意 :本程序实现的链表头结点(head)当中是存放数据的;为了保证每一个元素的id字段都不相同,所以定义了一个全局静态数据成员list_id;使用linux下的gcc进行编译)。程序如下:
/* 包含的头文件 */
#include
#include
/* 定义一个表示链表的结构体指针 */
struct list {
int id; /* 标识这个元素方便查找 */
char data[20]; /* 链表中包含的元素 */
struct list *next; /* 指向下一个链表的指针 */
};
/* 定义一个链表头部 */
static struct list *list_head = NULL;
/* 为了保证每一个链表元素的id不同,特意把id定义成一个全局静态变量 */
static int list_id = 0;
/** 将指定元素插入到聊表尾部
* head : 表示要插入元素的链表的头部的地址
* list : 表示要插入到链表中的元素
*/
static void list_add(struct list **head, struct list *list)
{
struct list *temp;
/* 判断链表是否为空 */
if(NULL == *head)
{
/* 为空 */
*head = list;
(*head)->next = NULL;
}
else
{
/* 不为空 */
temp = *head;
while(temp)
{
if(NULL == temp->next)
{
temp->next = list;
list->next = NULL;
}
temp = temp->next;
}
}
}
/** 遍历一个链表,打印链表中每个元素所包含的数据
* head : 表示要遍历的链表的头部的指针
*/
static void list_print(struct list **head)
{
struct list *temp;
temp = *head;
printf("list information :\n");
while(temp)
{
printf("\tlist %d : %s\n", temp->id, temp->data);
temp = temp->next;
}
}
/* 主函数,程序的入口 */
int main(int argc, char *argv[])
{
int i = 0;
struct list *lists = NULL;
/* 分配10个元素 */
lists = malloc(sizeof(struct list) * 10);
if(NULL == lists)
{
printf("malloc error!\n");
return -1;
}
/* 将分配的10个元素依次填充数据并加入到链表当中 */
for(i = 0; i < 10; i++)
{
lists[i].id = list_id++;
sprintf(lists[i].data, "TECH-PRO - %d", i);
list_add(&list_head, &lists[i]);
}
/* 遍历链表,把链表中每个元素的信息都打印出来 */
list_print(&list_head);
return 0;
}
程序与能行后如下:
二、单向链表的基本操作
链表的基本操作其实就是对链表中元素的操作。链表的基本操作包括链表中结点元素的添加、删除、修改和查看,简单来说就是增、删、改、查。实现链表插入的程序代码:
/** 将指定元素插入到聊表尾部
* head : 表示要插入元素的链表的头部的地址
* list : 表示要插入到链表中的元素
*/
static void list_add(struct list **head, struct list *list)
{
struct list *temp;
/* 判断链表是否为空 */
if(NULL == *head)
{
/* 为空 */
*head = list;
(*head)->next = NULL;
}
else
{
/* 不为空 */
temp = *head;
while(temp)
{
if(NULL == temp->next)
{
temp->next = list;
list->next = NULL;
}
temp = temp->next;
}
}
}
在main函数中田间如下代码:
struct list temp_list;
/* 填充这个结构体并加入链表当中 */
temp_list.id = list_id++;
sprintf(temp_list.data, "temp_list");
list_add(&list_head, &temp_list);
程序编译后结果如下:
2.2 在单向链表中删除元素
在链表删除一个元素结点是链表基本操作中相对复杂的。首先需要判断链表是否为空,当链表不为空时,要再次判断要删除的元素是否是头结点(head),如果是直接将下一个元素赋值给头结点,不过不是就需要遍历整个链表找到要删除的结点元素。(注:本算法实现的删除是通过对元素结点的id字段进行的)。
删除链表结点的程序代码:
/** 将指定元素从链表尾部删除
* head : 表示要删除元素的链表的头部的地址
* id : 表示要删除元素的标识
* 返回值 : 0-成功,-1-失败
*/
static int list_del(struct list **head, int id)
{
struct list *temp, *p;
temp = *head;
if(NULL == temp)
{
/* 链表为空 */
printf("链表为空!\n");
return -1;
}
else
{
/* 判断匹配的元素是否为链表头部的元素 */
if(id == temp->id) /* 是链表头部 */
{
*head = temp->next;
return 0;
}
else /* 不是链表头部 */
{
while(temp->next)
{
p = temp;
temp = temp->next;
if(id == temp->id)
{
p->next = temp->next;
return 0;
}
}
return -1;
}
}
return -1;
}
在mian函数中添加如下代码:
/* 删除链表中开始位置、中间位置、尾部的元素 */
list_del(&list_head, 0);
list_del(&list_head, 5);
list_del(&list_head, 10);
对程序进行编译结果如下所示,第0、5、10号元素都被删除,如下:
2.3 在单向链表中修改指定的元素
在链表中修改元素,通过对链表中元素进行遍历,找到要修改的元素对其内容分进行修改即可,实现链表元素的修改是相对容易的。(注 :对要修改的元素的定位是通过id字段来完成的)
链表中修改元素的程序如下:
/** 将指定id的元素所定义的内容进行修改
* head : 表示要改变元素的链表的头部的地址
* id : 表示要改变元素的标识
* content : 表示要改变的内容
* 返回值 : 0-成功,-1-失败
*/
static int list_chg(struct list **head, int id, char *content)
{
struct list *temp;
temp = *head; /* 将链表的头部赋值给临时聊表变量 */
while(temp) /* 对链表进行轮询 */
{
if(id == temp->id)
{
memset(temp->data, 0, sizeof(temp->data));
sprintf(temp->data, "%s", content);
temp->data[strlen(content)] = '\0';
return 0;
}
temp = temp->next;
}
return -1;
}
在main函数中添加如下代码:
/* 改变id为4的元素所对应的值为 "change!!!" */
list_chg(&list_head, 4, "change!!!");
与能行结果如下图所示:
2.4 对单向链表的元素进行查询操作
在链表中查询一个元素,根据链表头部对链表进行遍历即可。
链表中查询的程序代码:
/** 将指定id的元素所定义的内容进行查找
* head : 表示要查询元素的链表的头部的地址
* id : 表示要查询元素的标识
* 返回值 : 0-成功,-1-失败
*/
static int list_query(struct list **head, int id)
{
struct list *temp;
temp = *head; /* 将链表的头部赋值给临时聊表变量 */
while(temp) /* 对链表进行轮询 */
{
if(id == temp->id)
{
printf("list %d : %s\n", temp->id, temp->data);
return 0;
}
temp = temp->next;
}
/* 没有找到元素 */
printf("not finding!\n");
return -1;
}
程序与能行结果如下:
附:完整程序:
/* 包含的头文件 */
#include
#include
#include
/* 定义一个表示链表的结构体指针 */
struct list {
int id; /* 标识这个元素方便查找 */
char data[20]; /* 链表中包含的元素 */
struct list *next; /* 指向下一个链表的指针 */
};
/* 定义一个链表头部 */
static struct list *list_head = NULL;
/* 为了保证每一个链表元素的id不同,特意把id定义成一个全局静态变量 */
static int list_id = 0;
/** 将指定元素插入到链表尾部
* head : 表示要插入元素的链表的头部的地址
* list : 表示要插入到链表中的元素
*/
static void list_add(struct list **head, struct list *list)
{
struct list *temp;
/* 判断链表是否为空 */
if(NULL == *head)
{
/* 为空 */
*head = list;
(*head)->next = NULL;
}
else
{
/* 不为空 */
temp = *head;
while(temp)
{
if(NULL == temp->next)
{
temp->next = list;
list->next = NULL;
}
temp = temp->next;
}
}
}
/** 遍历一个链表,打印链表中每个元素所包含的数据
* head : 表示要遍历的链表的头部的指针
*/
static void list_print(struct list **head)
{
struct list *temp;
temp = *head;
printf("list information :\n");
while(temp)
{
printf("\tlist %d : %s\n", temp->id, temp->data);
temp = temp->next;
}
}
/** 将指定元素从链表尾部删除
* head : 表示要删除元素的链表的头部的地址
* id : 表示要删除元素的标识
* 返回值 : 0-成功,-1-失败
*/
static int list_del(struct list **head, int id)
{
struct list *temp, *p;
temp = *head;
if(NULL == temp)
{
/* 链表为空 */
printf("链表为空!\n");
return -1;
}
else
{
/* 判断匹配的元素是否为链表头部的元素 */
if(id == temp->id) /* 是链表头部 */
{
*head = temp->next;
return 0;
}
else /* 不是链表头部 */
{
while(temp->next)
{
p = temp;
temp = temp->next;
if(id == temp->id)
{
p->next = temp->next;
return 0;
}
}
return -1;
}
}
return -1;
}
/** 将指定id的元素所定义的内容进行修改
* head : 表示要改变元素的链表的头部的地址
* id : 表示要改变元素的标识
* content : 表示要改变的内容
* 返回值 : 0-成功,-1-失败
*/
static int list_chg(struct list **head, int id, char *content)
{
struct list *temp;
temp = *head; /* 将链表的头部赋值给临时聊表变量 */
while(temp) /* 对链表进行轮询 */
{
if(id == temp->id)
{
memset(temp->data, 0, sizeof(temp->data));
sprintf(temp->data, "%s", content);
temp->data[strlen(content)] = '\0';
return 0;
}
temp = temp->next;
}
return -1;
}
/** 将指定id的元素所定义的内容进行查找
* head : 表示要查询元素的链表的头部的地址
* id : 表示要查询元素的标识
* 返回值 : 0-成功,-1-失败
*/
static int list_query(struct list **head, int id)
{
struct list *temp;
temp = *head; /* 将链表的头部赋值给临时聊表变量 */
while(temp) /* 对链表进行轮询 */
{
if(id == temp->id)
{
printf("list %d : %s\n", temp->id, temp->data);
return 0;
}
temp = temp->next;
}
/* 没有找到元素 */
printf("not finding!\n");
return -1;
}
/* 主函数,程序的入口 */
int main(int argc, char *argv[])
{
int i = 0;
struct list *lists = NULL;
struct list temp_list;
/* 分配10个元素 */
lists = malloc(sizeof(struct list) * 10);
if(NULL == lists)
{
printf("malloc error!\n");
return -1;
}
/* 将分配的10个元素依次填充数据并加入到链表当中 */
for(i = 0; i < 10; i++)
{
lists[i].id = list_id++;
sprintf(lists[i].data, "TECH-PRO - %d", i);
list_add(&list_head, &lists[i]);
}
/* 填充这个结构体并加入链表当中 */
temp_list.id = list_id++;
sprintf(temp_list.data, "temp_list");
list_add(&list_head, &temp_list);
/* 删除链表中开始位置、中间位置、尾部的元素 */
list_del(&list_head, 0);
list_del(&list_head, 5);
list_del(&list_head, 10);
/* 改变id为4的元素所对应的值为 "change!!!" */
list_chg(&list_head, 4, "change!!!");
/* 查询链表中id为4的元素结点的内容 */
list_query(&list_head, 4);
return 0;
}
例题:从尾到头打印一个链表。
2.尾部添加无返回值void addTail(ListNode** Head,int value)(用到了指针的指针ListNode**head,创建一个节点pNew并赋初值。if(*Head==NULL){*Head=pNew;else创建行动指针p并指向头结点,找到插入节点的头一个接点,在它的后面插入节点。})。
3.删除一个已知值的节点返回空,void removeNode(ListNode** Head,int value)(同样用到指针的指针,if(Head==NULL&&*Head==NULL)初始化要删除的指针toBeDelete,if(*Head==value){toBeDelete=*Head,*Head=*Head->next)},else,初始化行动指针p并指向头指针,找到要删除的节点的头一个节点p,if(p->next!=null&&p->next->value==value){ toBeDelete=p->next,p-next=p->next->next}。)
4.打印(从头到尾)返回空,void printLink(ListNode* pHead)(初始化行动指针p并指向头节点,当满足p不为空的时候,输出p->value,并且移动p=p->next)6.打印(从尾到头(递归)),返回空。void printLinkReservese_Recurse(ListNode *pHead)(if(pHead!=NULL){if(pHead->next!=NULL) printLinkReservse_Reservese_Recurse(pHead->next);}输出pHead->value.)
#include
#include
#include
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
void RemoveNode(ListNode ** pHead,int value)
{
if(pHead==NULL||*pHead==NULL)
return;
ListNode* toBeDelete=NULL;
if((*pHead)->m_nValue==value)
{
toBeDelete=*pHead;
*pHead=(*pHead)->m_pNext;
}
else
{
ListNode*p=*pHead;
while(p->m_pNext!=NULL&&p->m_pNext->m_nValue!=value)
p=p->m_pNext;
if(p->m_pNext!=NULL&&p->m_pNext->m_nValue==value)
{
toBeDelete=p->m_pNext;
p->m_pNext=p->m_pNext->m_pNext;
}
//这一步很重要,防止操作已经释放的内存空间
if(toBeDelete!=NULL)
{
delete toBeDelete;
toBeDelete=NULL;
}
}
}
void AddToTail(ListNode ** pHead,int value)
{
ListNode * pNew=new ListNode();
pNew->m_nValue=value;
pNew->m_pNext=NULL;
if(*pHead==NULL)
{
*pHead=pNew;
}
else
{
ListNode * pNode = *pHead;
while(pNode->m_pNext != NULL)
pNode=pNode->m_pNext;
pNode->m_pNext=pNew;
}
}
ListNode * CreateLink(int a[],int k)
{
ListNode * Head=NULL,*q=NULL;
for(int i=0;im_nValue=a[i];
pNew->m_pNext=NULL;
if(Head==NULL)
{
Head=pNew;
q=pNew;
}
else
{
q->m_pNext=pNew;
q=q->m_pNext;
}
}
return Head;
}
//从头到尾打印列表
void printLink(ListNode * pHead)
{
ListNode *p=pHead;
while(p)
{
cout<m_nValue<<" ";
p=p->m_pNext;
}
cout< nodes;
ListNode *p=pHead;
while(p)
{
nodes.push(p);
p=p->m_pNext;
}
while(!nodes.empty())
{
p=nodes.top();
cout<m_nValue<<" ";
nodes.pop();
}
}
//从尾到头打印链表(用递归,隐式调用栈)。
void printListReversesingly_Recursively(ListNode * pHead)
{
if(pHead!=NULL)
{
if(pHead->m_pNext!=NULL)
{
printListReversesingly_Recursively(pHead->m_pNext);
}
}
cout<m_nValue<<" ";
}
void main()
{
int a[]={1,2,3};
ListNode * Head=CreateLink(a,3);
printLink(Head);
AddToTail(&Head,6);
printLink(Head);
printListReversesingly_Recursively(Head);
cout<
本文链接: http://blog.csdn.net/tech_pro