前几天找实习的时候,一个面试官给我留了一个题,做一个链表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。