Chinaunix首页 | 论坛 | 博客
  • 博客访问: 176093
  • 博文数量: 63
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 181
  • 用 户 组: 普通用户
  • 注册时间: 2016-02-25 15:50
文章分类
文章存档

2020年(1)

2016年(62)

我的朋友

分类: C/C++

2016-03-15 14:25:03


本文是stanford cs library中两篇关于linked list的文章合体版

我觉得这两篇讲linked list的文章写的非常非常好,所以在博客里自己写一下,也算是温习巩固数据结构的知识了


本文代码下载:


本文结合一个实际的例子来解释什么是linked list,如何使用linked list


不过在解释linked list之前,必须先要了解指针的相关知识,这里先做一个简单回顾一下指针


1.指针存储了对另一个变量的引用。 指针的值是一个地址。


2.在C语言中,   *  这个符号被称为解析符号,它用来取得指针所指向的那个变量的值,举个例子

[cpp] view plain copy
  1. #include  
  2. void main ()  
  3. {  
  4.     int a=3;  
  5.   
  6.     int* p = &a;  
  7.   
  8.     printf("*p=%d\n",*p);  
  9. }  

上面这个例子在printf打印函数中,*p就是把指针p指向的变量a的值取出来

我记得我在学习C语言指针的时候,一直搞不清楚 * 这个符号,一个原因就是 int* p=&a  在这行代码中, int* p是申明了一个指针,这一行代码也能够拆成两行来写

[cpp] view plain copy
  1. int *p;  
  2. p=&a;??  

但是请注意,int* p和 int *p这两种写法是一样的,都是申明了一个指向整形变量的指针,但是 int* p更好,因为 int* 表示这是一个指向整形的指针,名字叫做p,而 int *p 的写法容易让人误解为指针的名字叫做 *p,我当时学C语言时在这个问题上纠结了很久


3. & 这个符号叫做取地址符,在上面的例子中, int *p = &a; 这行代码就是把变量 a 的地址赋给指针 p


关于指针内容其实非常多,这里只是一个简单的回顾,想看更多关于指针的资料,我推荐大家看这篇文章,, 这篇文章是stanford大学的教授写的,我看完以后,有一种豁然开朗的感觉


linked list是一种和数组有几分相似的数据结构,我相信大家都非常了解数组,数组有3个很明显的缺点:

1. 数组的长度是固定的。比如申明了一个长度为100的整形数组 int a[100], 数组a的长度就是100,如果这时需要存储200个数字,那么a就用不了了

2. 数组造成存储空间的浪费。如果这时数组a只存储了 1个整数,那么它的其他99个位置就相当于浪费了

3. 往数组里插入元素的开销是非常大的。比如要在数组的第一个元素的位置上插入1个整数,那就得把数组其他位置上的元素都向后移动一位


正因为数组有以上的这些缺点,所以产生了linked list,linked list很好的克服了以上的三个缺点


首先,定义一个名字叫做node的结构体

[cpp] view plain copy
  1. struct node {  
  2.     int data;  
  3.     struct node* next;  
  4. };  
这个结构体中包含一个整数,和一个指向结构体node的指针

接下来我们写一个函数,生成一个linked list

[cpp] view plain copy
  1. struct node* BuildOneTwoThreeFourFive() {  
  2.     struct node* head = NULL;  
  3.     struct node* second = NULL;  
  4.     struct node* third = NULL;  
  5.     struct node* forth = NULL;  
  6.     struct node* fifth = NULL;  
  7.   
  8.     head = malloc(sizeof(struct node));  
  9.     second = malloc(sizeof(struct node));  
  10.     third = malloc(sizeof(struct node));  
  11.     forth = malloc(sizeof(struct node));  
  12.     fifth = malloc(sizeof(struct node));  
  13.   
  14.     head->data = 1;  
  15.     head->next = second;  
  16.   
  17.     second->data = 2;  
  18.     second->next = third;  
  19.   
  20.     third->data = 3;  
  21.     third->next = forth;  
  22.   
  23.     forth->data = 4;  
  24.     forth->next = fifth;  
  25.   
  26.     fifth->data = 5;  
  27.     fifth->next= NULL;  
  28.     return head;  
  29. }  
现在,这个linked list就会有5个元素,如果打印出这个linked list的data值,那就是 {1,2,3,4,5}

求linked list长度的函数

[cpp] view plain copy
  1. int Length(struct node* head) {  
  2.     struct node* current = head;  
  3.     int count = 0;  
  4.   
  5.     while(current != NULL) {  
  6.         count++;  
  7.         current = current->next;  
  8.     }  
  9.   
  10.     return count;  
  11. }  

往这个linked list里添加一个节点,这个节点要在linked list的头部,

[cpp] view plain copy
  1. void Push(struct node** headRef, int data) {  
  2.     struct node* newNode = malloc(sizeof(struct node));  
  3.   
  4.     newNode->data = data;  
  5.     newNode->next = *headRef;  
  6.     *headRef = newNode;  
  7. }  
这个Push函数是一个比较难理解的地方,因为它的参数列表有一个参数 struct node** headRef。


我们都知道, struct node* head表示的是一个指向struct node的指针,名字是head

那么,struct node** headRef表示的就是一个指向struct node的指针的指针,名字是headRef

当然,这个地方为什么用指针的指针,还是很有讲究的,至于为什么,可以参考本文开头时提到的第一篇文章,由于篇幅有限,这里不做解释了


写一个函数,函数有两个参数,一个是linked list,另一个是一个整数,查询在这个linked list里有几个元素的data值和整个整数相等

[cpp] view plain copy
  1. int Length(struct node* head) {  
  2.     struct node* current = head;  
  3.     int count = 0;  
  4.   
  5.     while(current != NULL) {  
  6.         count++;  
  7.         current = current->next;  
  8.     }  
  9.   
  10.     return count;  
  11. }  

取得linked list中的第N个元素的data值

[cpp] view plain copy
  1. int GetNth(struct node* head, int index) {  
  2.     struct node* current = head;  
  3.     int count = 0;  
  4.       
  5.     while(current != NULL) {  
  6.         if(count == index) return (current->data);  
  7.         count++;  
  8.         current = current->next;  
  9.     }  
  10.   
  11.     assert(0);  
  12. }  
删除这个linked list

[cpp]
 view plain copy
  1. void DeleteList(struct node** headRef) {  
  2.     struct node* current = *headRef;  
  3.     struct node* next;  
  4.   
  5.     while(current != NULL) {  
  6.   
  7.         next = current->next;  
  8.         free(current);  
  9.         current = next;  
  10.     }  
  11.   
  12.     *headRef = NULL;  
  13. }  

取出这个linked list里头部的那个节点的data值,并且销毁第一个节点

[cpp] view plain copy
  1. int Pop(struct node** headRef) {  
  2.     struct node* head;  
  3.     int result;  
  4.   
  5.     head = *headRef;  
  6.   
  7.     assert(head != NULL);  
  8.   
  9.     result = head->data;  
  10.     *headRef = head->next;  
  11.   
  12.     free(head);  
  13.     return result;  
  14. }  
在第n个节点的位置上插入一个节点

[cpp]
 view plain copy
  1. void InsertNth(struct node** headRef, int index, int data) {  
  2.     if(index == 0) Push(headRef,data);  
  3.   
  4.     else {  
  5.         struct node* current = *headRef;  
  6.         int i;  
  7.   
  8.         for(i=0; i
  9.             assert(current != NULL);  
  10.             current = current->next;  
  11.         }  
  12.   
  13.         assert(current != NULL);  
  14.   
  15.         Push(&(current->next),data);  
  16.   
  17.     }  
  18. }  

假设一个linked list已经按照data的升序排列好了,这时往这个linked list添加一个节点,这个节点会被加入的正确的位置上

[cpp] view plain copy
  1. void SortedInsert(struct node** headRef, struct node* newNode) {      
  2.     if(*headRef == NULL || (*headRef)->data >= newNode->data) {  
  3.                 newNode->next = *headRef;  
  4.         *headRef = newNode;  
  5.     }  
  6.     else {  
  7.         struct node* current = *headRef;  
  8.         while(current->next != NULL && current->next->datadata) {  
  9.             current = current->next;  
  10.         }  
  11.         newNode->next = current->next;  
  12.         current->next = newNode;  
  13.     }  
  14. }  

linked list插入排序

[cpp] view plain copy
  1. void InsertSort(struct node** headRef) {  
  2.     struct node* result = NULL;  
  3.     struct node* current = *headRef;  
  4.     struct node* next;  
  5.   
  6.     while(current != NULL) {  
  7.         next = current->next;  
  8.         SortedInsert(&result, current);  
  9.         current = next;  
  10.     }  
  11.   
  12.     *headRef = result;  
  13. }  

把一个链表加入到另一个链表的尾部

[cpp] view plain copy
  1. void Append(struct node** aRef, struct node** bRef) {  
  2.     struct node* current;  
  3.     if(*aRef == NULL) {  
  4.         *aRef = *bRef;  
  5.     }  
  6.     else {  
  7.         current = *aRef;  
  8.         while(current->next != NULL)  
  9.             current = current->next;  
  10.   
  11.         current->next = *bRef;  
  12.     }  
  13.   
  14.     *bRef = NULL;  
  15. }  

把一个链表对半分,比如,链表是{2,3,5,7,11}, 分割后产生两个链表,第一个{2,3,5}, 第二个{7,11}

[cpp] view plain copy
  1. void FrontBackSplit(struct node* source, struct node** frontRef, struct node** backRef) {  
  2.     int len = Length(source);  
  3.   
  4.     int i;  
  5.     struct node* current = source;  
  6.   
  7.     if(len<2) {  
  8.         *frontRef = source;  
  9.         *backRef = NULL;  
  10.     }  
  11.     else {  
  12.         int hopCount  = (len-1)/2;  
  13.         for(i=0; i
  14.             current = current->next;  
  15.   
  16.         *frontRef = source;  
  17.         *backRef = current->next;  
  18.         current->next = NULL;  
  19.     }  
  20. }  

去除链表中的重复节点

[cpp] view plain copy
  1. void RemoveDuplicates(struct node* head) {  
  2.     struct node* current = head;  
  3.     if(current == NULL) return;  
  4.   
  5.     // compare current node with next node  
  6.     while(current->next != NULL) {  
  7.         if(current->data == current->next->data) {  
  8.             struct node* nextNext = current->next->next;  
  9.             free(current->next);  
  10.             current->next = nextNext;  
  11.         }  
  12.         else {  
  13.             current = current->next;  
  14.         }  
  15.     }  
  16. }  

把第二个链表的第一个节点添加到第一个链表的头节点上

[cpp] view plain copy
  1. void MoveNode(struct node** destRef, struct node** sourceRef) {  
  2.     struct node* newNode = *sourceRef;  
  3.     assert(newNode != NULL);  
  4.   
  5.     *sourceRef = newNode->next;  
  6.   
  7.     newNode->next = *destRef;  
  8.     *destRef = newNode;  
  9. }  

AlternatingSplit

每隔一个节点取出的合成一个linked list,比如,{a,b,c,d,e,f}在 AlternatingSplit()后生成两个链表 {a,c,e}   和 {b,d,f}

[cpp] view plain copy
  1. void AlternatingSplit(struct node* source, struct node** aRef, struct node** bRef) {  
  2.     struct node* a = NULL;  
  3.     struct node* b = NULL;  
  4.   
  5.     struct node* current = source;  
  6.     while(current != NULL) {  
  7.         MoveNode(&a,¤t);  
  8.         if(current!=NULL)  
  9.             MoveNode(&b,¤t);  
  10.     }  
  11.   
  12.     *aRef = a;  
  13.     *bRef = b;  
  14. }  

ShuffleMerge

这个函数的目的需要举例说明,比如,两个链表{1,2,3}和{7,13,1},两个链表shufflemerge后的结构就是{1,7,2,13,3,1}

也就是分别取出每个从每个链表头节点合并成一个链表

[cpp] view plain copy
  1. struct node* ShuffleMerge(struct node* a, struct node* b) {  
  2.     struct node* result;  
  3.     struct node* recur;  
  4.   
  5.     if(a == NULL) return b;  
  6.     else if(b == NULL) return a;  
  7.     else {  
  8.         recur = ShuffleMerge(a->next, b->next);  
  9.   
  10.         result = a;  
  11.         a->next = b;  
  12.         b->next = recur;  
  13.         return result;  
  14.     }  
  15. }  

SortedMerge

假设两个链表都是按照升序递增的,把两个链表合并,并且合并后的链表也是按照升序排列的

[cpp] view plain copy
  1. struct node* SortedMerge(struct node* a, struct node* b) {  
  2.     struct node dummy;  
  3.     struct node* tail = &dummy;  
  4.   
  5.     dummy.next = NULL;  
  6.   
  7.     while(1) {  
  8.         if(a == NULL) {  
  9.             tail->next = b;  
  10.             break;  
  11.         }  
  12.   
  13.         else if (b == NULL) {  
  14.             tail->next = a;  
  15.             break;  
  16.         }  
  17.         if(a->data <= b->data) {  
  18.             MoveNode(&(tail->next),&a);  
  19.         }  
  20.         else {  
  21.             MoveNode(&(tail->next),&b);  
  22.         }  
  23.         tail = tail->next;  
  24.     }  
  25.   
  26.     return dummy.next;  
  27. }  
MergeSort


链表的归并排序

[cpp] view plain copy
  1. void MergeSort(struct node** headRef) {  
  2.     struct node* head = *headRef;  
  3.     struct node* a;  
  4.     struct node* b;  
  5.   
  6.     if((head==NULL) || (head->next)==NULL)  
  7.         return;  
  8.   
  9.     FrontBackSplit(head,&a,&b);  
  10.   
  11.     MergeSort(&a);  
  12.     MergeSort(&b);  
  13.   
  14.     *headRef = SortedMerge(a,b);  
  15. }  

SortedIntersect

假设两个链表是按照升序排列的,找出两个链表相等的节点

[cpp] view plain copy
  1. struct node* SortedIntersect(struct node* a, struct node* b) {  
  2.     struct node dummy;  
  3.     struct node* tail = &dummy;  
  4.   
  5.     dummy.next = NULL;  
  6.   
  7.     while(a != NULL && b != NULL) {  
  8.         if(a->data == b->data) {  
  9.             Push((&tail->next),a->data);  
  10.             tail = tail->next;  
  11.             a = a->next;  
  12.             b = b->next;  
  13.         }  
  14.         else if(a->data < b->data) {  
  15.             a = a->next;  
  16.         }  
  17.         else {  
  18.             b = b->next;  
  19.         }  
  20.     }  
  21.   
  22.     return dummy.next;  
  23. }  

Reverse

链表的倒置

这个是非递归版本

[cpp] view plain copy
  1. void Reverse(struct node** headRef) {  
  2.     struct node* result = NULL;  
  3.     struct node* current = *headRef;  
  4.     struct node* next;  
  5.   
  6.     while(current != NULL) {  
  7.         next = current->next;  
  8.   
  9.         current->next = result;  
  10.         result = current;  
  11.   
  12.         current = next;  
  13.     }  
  14.   
  15.     *headRef = result;  
  16. }  

下面这个是递归版本

[cpp] view plain copy
  1. void RecursiveReverse(struct node** headRef) {  
  2.     struct node* first;  
  3.     struct node* rest;  
  4.   
  5.     if(*headRef == NULL) return;  
  6.   
  7.     first = *headRef;         
  8.     rest = first->next;        
  9.           
  10.     if(rest == NULL) return;      
  11.   
  12.     RecursiveReverse(&rest);  
  13.   
  14.     first->next->next = first;  
  15.     first->next = NULL;  
  16.   
  17.     *headRef = rest;  
  18. }  


倒置的操作比较复杂,需要画图去跟指针的变化

当然,我觉得最好的方法还是用gdb单步调试,跟踪指针变化


阅读(1322) | 评论(0) | 转发(0) |
0

上一篇:图像处理C实现

下一篇:gcc和g++的区别

给主人留下些什么吧!~~