您的位置:首页 > 大数据 > 人工智能

list 链表及list_add_tail 双向链表实现分析

2018-01-08 15:07 627 查看
前几天找实习的时候,一个面试官给我留了一个题,做一个链表demo,要求实现创建、插入、删除等操作。

链表是一种常见的数据结构,它是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

我是用C++代码来写的。首先,定义一个linklist.h文件,该文件定义了链表的结点和链表支持的方法。如下所示:

[cpp] view
plain copy

//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文件,是链表方法的实现。如下所示:

[cpp] view
plain copy

//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,用来测试链表功能,如下所示:

[cpp] view
plain copy

// 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()函数,现在对其进行分析:

[cpp] view
plain copy

<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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: