链表
关键字:非连续存储,动态生成,指针连接,数据与和指针域。
分类
按头节点:
- 带头链表(头节点无数据域)
- 不带头链表
按节点组织形式:
- 单向链表
- 双向链表
- 循环链表
按存储结构:
- 静态链表,用数组实现或栈中分配内存,连续存储,容量固定。
- 动态链表,用
malloc()
等内存申请函数实现。
编程元素
pHead,pCurrent,pPrev,pNext.
,还有新建节点pMalloc
.
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
typedef struct node{
int data;
struct node *next;
}linklist;
linklist *Creatlist();
linklist *Find(linklist *head,int x);
int add_to_end(linklist *p,int data);
int insert(linklist *head,int originData,int data);
int delNode(linklist *head,int data);
void linklist_output(linklist *head);
int inverse(linklist *head);
int linklist_destroy(linklist **head);
int main(void)
{
int data;
linklist *pHead = NULL;
int ret;
pHead = Creatlist();
if(pHead == NULL){
printf("CreatList() err\n");
return 1;
}
linklist_output(pHead);
ret = insert(pHead,2,5);
if(ret != 0)
{
return 0;
}
linklist_output(pHead);
ret = inverse(pHead);
if(ret != 0)
{
return 0;
}
linklist_output(pHead);
ret = linklist_destroy(&pHead);
if(ret != 0)
{
return 0;
}
return 0;
}
//initial the linklist
linklist *Creatlist()
{
/*
with a head node;
single direction;
loop creation;
input ends with -1.
return the address of the head node.
*/
int data;
linklist *pHead = NULL,*pMalloc = NULL,*pCurrent = NULL;
pHead=(linklist*)malloc(sizeof(linklist));
if(pHead == NULL)
{
return NULL;
}
pHead->data = 0;
pHead->next = NULL;
pCurrent = pHead;
while(data != -1)
{
pMalloc = (linklist*)malloc(sizeof(linklist));
if(pMalloc == NULL)
{
linklist_destroy(&pHead);
return pHead;
}
printf("input data(linklist ends with -1):");
scanf("%d",&data);
fflush(stdin);
pMalloc->data = data;
pMalloc->next = NULL;
pCurrent->next=pMalloc;
pCurrent=pMalloc;
}
pCurrent->next=NULL;
return pHead;
}
void linklist_output(linklist *head)
{
linklist *pCurrent = NULL;
if(head == NULL)
{
printf("linklist_output() arg can't be NULL.\n");
return ;
}
pCurrent = head->next;
while(pCurrent)
{
printf("%d ",pCurrent->data);
pCurrent = pCurrent->next;
}
printf("\n");
}
int add_to_end(linklist *head,int data)
{
linklist *pMalloc = NULL,*pRear = NULL;
pMalloc = (linklist*)malloc(sizeof(linklist));
if(pMalloc == NULL)
{
printf("InsertAfter() : malloc() error.\n");
return -1;
}
pMalloc->data = data;
pMalloc->next = NULL;
pRear = head;
while(pRear->next != NULL){
pRear = pRear->next;
}
pRear->next = pMalloc;
return 0;
}
int insert(linklist*head,int originData,int data)
{
linklist *pPre = NULL,*pCurrent = NULL,*pMalloc = NULL;
if(head == NULL)
{
printf("insert() arg linklist*head can't be NULL.\n");
return -1;
}
pPre = head;
pCurrent = head->next;
pMalloc = (linklist*)malloc(sizeof(linklist));
if(pMalloc == NULL)
{
printf("InsertAfter() : malloc() error.\n");
return -1;
}
pMalloc->data = data;
pMalloc->next = NULL;
/*
if originData is found,pMalloc will replace it,
or pMalloc will be added to the end.
*/
while(pCurrent)
{
if(pCurrent->data == originData)
{
break;
}
pPre = pCurrent;
pCurrent = pCurrent->next;
}
pPre->next = pMalloc;
pMalloc->next = pCurrent;
return 0;
}
int delNode(linklist *head,int data_to_del)
{
linklist *pPrev = NULL, *pCurrent = NULL;
if(head == NULL)
{
printf("delNode() arg linklist*head can't be NULL.\n");
return -1;
}
pPrev = head;
pCurrent = head->next;
while(pCurrent)
{
if(pCurrent->data == data_to_del)
{
break;
}
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
if(pCurrent == NULL)
{
printf("%d not found.\n",data_to_del);
return -1;
}
pPrev->next = pCurrent->next;
if(pCurrent != NULL)
{
free(pCurrent);
pCurrent = NULL;
}
return 0;
}
int inverse(linklist *head){
linklist *pPrev = NULL;
linklist *pCurrent = NULL;
linklist *pNext = NULL;
if(head == NULL)
{
printf("delNode() arg linklist*head can't be NULL.\n");
return -1;
}
if(head->next == NULL)
{
printf("the linklist has just one head node.\n");
return -1;
}
pPrev = head;
pCurrent = head->next;
while(pCurrent){
pNext = pCurrent->next;
pCurrent->next = pPrev;
pPrev = pCurrent;
pCurrent = pNext;
//pNext = pCurrent->next; DON'T BE HERE锛?
}
/*
---->O----->O------>NULL
pPrev pCurrent/pNext
*/
head->next->next = NULL;
head->next = pPrev;
return 0;
}
int linklist_destroy(linklist **head)
{
linklist *pHead,*pNext = NULL;
if(head == NULL)
{
printf("linklist_destroy() arg linklist*head can't be NULL.\n");
return -1;
}
pHead = *head;
while(pHead)
{
pNext = pHead->next;
pHead->next = NULL;
free(pHead);
pHead = pNext;
}
*head = NULL;
return 0;
}
linux内核链表
上面的部分是传统链表,节点包含数据域和指针域。
Linus在28岁时发明了linux内核链表,让数据域包含指针域,进而实现不同结构体链接起来。
数据域不能包含万事万物,那就让万事万物包含链表节点。
这种链表需要手动实现计算成员偏移量。
//compute the offset of the member
#define offsetof(TYPE,MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)
struct list_head{
struct list_head *next,*prev;
}
struct Foo{
int id;
struct list_head list;
}
struct Bar{
int id;
struct list_head list;
}
int main()
{
struct list_head head,*plist;
struct Foo foo;
struct Bar bar;
INIT_LIST_HEAD(&head);
list_add(&foo.list,head);
list_add(&bar.list,head);
list_for_each(plist,&head){
struct Foo *node = list_entry(plist,struct Foo,list);
printf("%d \n",node->id);
}
return 0;
}
通用链表
基于linux内核链表,还可以做一点优化,让指针域地址和结构体地址重合。这种链表在实际中大量应用。
struct Foo{
struct list_head list;
int id;
}
可以把这种链表编译为动态库随时调用。