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。
链表是一种常见的数据结构,它是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
我是用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。
相关文章推荐
- list_add_tail 双向链表实现分析 .
- list_add_tail 双向链表实现分析
- 数据结构:链表List的实现与代码分析
- C++的标准模板库STL中实现的数据结构之链表std::list的分析与使用
- [Java算法分析与设计]单向链表(List)的实现和应用
- 容器第四课,JDK源代码分析,自己实现LinkedList,双向链表的概念_节点定义
- list_add_tail 添加双向链表结点讲解
- C++的标准模板库STL中实现的数据结构之链表std::list的分析与使用
- 【数据结构】链表LinkedList分析与关键实现
- Android版数据结构与算法(三):基于链表的实现LinkedList源码彻底分析
- STL源码分析----神奇的 list 的 sort 算法实现
- .分析以下需求,并用代码实现 1.定义List集合,存入多个字符串 2.删除集合元素字符串中包含0-9数字的字符串 只要字符串中包含0-9中的任意一个数字就需
- 数组转换为List后add或remove出现UnsupportedOperationException异常分析
- 链表(LinkedList) Java 语言实现
- C# 泛型类的实现,链表的封装LinkedListNode
- [置顶] 用JavaScript来实现链表LinkedList
- 双向链表(Dlist) 用模板实现
- LeetCode:Linked List Cycle--链表快慢指针实现求链表中位和查找链表中是否存在环
- 集合框架源码分析三(实现类篇ArrayList,LinkedList,HashMap)
- [Java算法分析与设计]线性结构与顺序表(List)的实现应用