前几天找实习的时候,一个面试官给我留了一个题,做一个链表demo,要求实现创建、插入、删除等操作。
链表是一种常见的数据结构,它是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
我是用C++代码来写的。首先,定义一个linklist.h文件,该文件定义了链表的结点和链表支持的方法。如下所示:
- //linklist.h:定义链表结点和方法。
- #include <string>
- using namespace std;
- struct Info
- {
- string name; //姓名
- int id; //学号
- };
- //链表定义
- struct Node
- {
- Info val;
- Node *next;
- Node(Info x):val(x),next(NULL) {}
- };
- class LinkList
- {
- public:
- //构造函数
- LinkList();
- //在链表头部插入结点
- void InsertHead(Info val);
- //插入结点
- void Insert(Info val,int pos);
- //删除结点
- void Remove(Info val);
- //得到链表长度
- int Length();
- //链表反序
- void Reverse();
- //查找结点位置
- int Find(Info val);
- //打印链表
- void Print();
- //析构函数
- ~LinkList();
- private:
- Node *head;
- int length;
- };
然后,定义一个linklist.cpp文件,是链表方法的实现。如下所示:
- //linklist.cpp:链表方法的实现。
- #include "stdafx.h"
- #include <iostream>
- #include "linklist.h"
- using namespace std;
- //构造函数
- LinkList::LinkList()
- {
- head = NULL;
- length = 0;
- }
- //析构函数
- LinkList::~LinkList()
- {
- Node *temp;
- for(int i=0;i<length;i++)
- {
- temp=head;
- head=head->next;
- delete temp;
- }
- }
- //得到链表长度
- int LinkList::Length()
- {
- return length;
- }
- //在链表头部插入结点
- void LinkList::InsertHead(Info val)
- {
- Insert(val,0);
- }
- //插入结点
- void LinkList::Insert(Info val,int pos)
- {
- if(pos<0)
- {
- cout<<"pos must be greater than zero"<<endl;
- return;
- }
- int index = 1;
- Node *temp = head;
- Node *node = new Node(val);
- if(pos == 0)
- {
- node->next = temp;
- head = node;
- length++;
- return;
- }
- while(temp!=NULL && index<pos)
- {
- temp=temp->next;
- index++;
- }
- if(temp == NULL)
- {
- cout<<"Insert failed"<<endl;
- return;
- }
- node->next = temp->next;
- temp->next = node;
- length++;
- }
- //删除结点
- void LinkList::Remove(Info val)
- {
- int pos = Find(val);
- if(pos == -1)
- {
- cout<<"Delete failed"<<endl;
- return;
- }
- if(pos == 1)
- {
- head = head->next;
- length--;
- return;
- }
- int index = 2;
- Node *temp = head;
- while(index < pos)
- temp = temp->next;
- temp->next = temp->next->next;
- length--;
- }
- //查找结点位置
- int LinkList::Find(Info val)
- {
- Node *temp = head;
- int index = 1;
- while(temp!=NULL)
- {
- if(temp->val.name == val.name && temp->val.id == val.id)
- return index;
- temp = temp->next;
- index ++;
- }
- return -1; //不存在返回-1
- }
- //链表反序
- void LinkList::Reverse()
- {
- if(head==NULL)
- return;
- Node *curNode=head,*nextNode=head->next,*temp;
- while(nextNode!=NULL)
- {
- temp=nextNode->next;
- nextNode->next=curNode;
- curNode=nextNode;
- nextNode=temp;
- }
- head->next=NULL;
- head=curNode;
- }
- //打印链表
- void LinkList::Print()
- {
- if(head == NULL)
- {
- cout<<"LinkList is empty"<<endl;
- return;
- }
- Node *temp = head;
- while(temp!=NULL)
- {
- cout<<temp->val.name<<","<<temp->val.id<<endl;
- temp=temp->next;
- }
- cout<<endl;
- }
最后,定义一个main.cpp,用来测试链表功能,如下所示:
- // main.cpp : 测试链表功能。
- #include "stdafx.h"
- #include <iostream>
- #include <string>
- #include "linklist.h"
- using namespace std;
- int _tmain(int argc, _TCHAR* argv[])
- {
- LinkList head;
- Info val1,val2,val3,val4;
- val1.id =1,val1.name="Kevin",val2.id=2,val2.name="Cathy",val3.id=3,val3.name="Lucy",val4.id=4,val4.name="Gravin";
- //测试插入功能
- cout<<"Insert test:"<<endl;
- head.InsertHead(val1);
- head.Print();
- head.Insert(val2,1);
- head.Print();
- head.Insert(val3,4);
- head.Print();
- head.InsertHead(val3);
- head.Insert(val4,2);
- head.Print();
- //测试反序功能
- cout<<"reverse test:"<<endl;
- head.Reverse();
- cout<<"reversed linklist is:"<<endl;
- head.Print();
- //测试删除功能
- cout<<"remove test:"<<endl;
- cout<<"the length of linklist is:"<<endl;
- cout<<head.Length()<<endl;
- head.Remove(val4);
- head.Print();
- cout<<"the length of linklist is:"<<endl;
- cout<<head.Length()<<endl;
- head.Remove(val4);
- head.Print();
- return 0;
- }
测试结果如下图:
转自http://www.xuebuyuan.com/1389026.html
在看内核v4l2示例代码driver/media/video/vivi.c时 ,看到list_add_tail()函数,现在对其进行分析:
- <span style="font-size:24px;">struct list_head {
- struct list_head *next, *prev;
- };
- list_add_tail(&buf->vb.queue, &vid->active);
- /**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
- static <span style="color:#3333ff;">__inline__</span> void list_add_tail(struct list_head *_new, struct list_head *head)
- {
- <span style="color:#3333ff;">__list_add(_new, head->prev, head);</span>
- }
- /*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
- static __inline__ void __list_add(struct list_head * _new,
- struct list_head * prev,
- struct list_head * next)
- {
- <span style="color:#3333ff;"> next->prev = _new;
- _new->next = next;
- _new->prev = prev;
- prev->next = _new;</span>
- }
- </span>
很多地方说:这个函数完成的功能就是添加一个新的结点在head的左边,其实不然,它是从右向左在head->priv和head两个节点之间插入_new。
假设刚开始建立链表,只有struct list_head *head,
那么前两句话有用:将next->prev = _new;
_new->next = next;
这就是将new节点添加到head 节点的左边,那么接 下来两句没用: _new->prev = prev; prev->next = _new;
如果head左边已近有了其他节点,那么调用list_add_tail()函数后,前边两句的功能一样,都是把新的节点添加在head左边,而后两句就是把新节点添加在原来head之前节点(head->priv)右边,这样就串起来了。
那list_add就反过来,把新的节点添加在head和head之后的节点(head->next)之间;
关于list_add和list_add_tail建立栈和FIFO:
list_add和list_add_tail都是在head两边插入新的节点,所以list_add先插入的节点向右移,head->next是最后插入的节点,list_add_tail先插入的节点向左移,head->next是最先插入的节点;
遍历链表都是从head开始向下,所以用list_add建立的链表先访问的是最后插入的节点,类似于栈;list_add_tail建立的链表先访问的是最先插入的节地点,类似于FIFO。