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

  • Post author:
  • Post category:其他


前几天找实习的时候,一个面试官给我留了一个题,做一个链表demo,要求实现创建、插入、删除等操作。

链表是一种常见的数据结构,它是一种物理


存储单元


上非连续、非顺序的


存储结构





数据元素


的逻辑顺序是通过链表中的


指针


链接次序实现的



链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储


数据元素


的数据域,另一个是存储下一个结点地址的


指针


域。

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



  1. //linklist.h:定义链表结点和方法。





  2. #include <string>





  3. using




    namespace


    std;




  4. struct


    Info


  5. {

  6. string name;

    //姓名





  7. int


    id;


    //学号




  8. };


  9. //链表定义





  10. struct


    Node


  11. {

  12. Info val;

  13. Node *next;

  14. Node(Info x):val(x),next(NULL) {}

  15. };



  16. class


    LinkList


  17. {


  18. public


    :



  19. //构造函数




  20. LinkList();


  21. //在链表头部插入结点





  22. void


    InsertHead(Info val);



  23. //插入结点





  24. void


    Insert(Info val,


    int


    pos);



  25. //删除结点





  26. void


    Remove(Info val);



  27. //得到链表长度





  28. int


    Length();



  29. //链表反序





  30. void


    Reverse();



  31. //查找结点位置





  32. int


    Find(Info val);



  33. //打印链表





  34. void


    Print();



  35. //析构函数




  36. ~LinkList();


  37. private


    :


  38. Node *head;


  39. int


    length;


  40. };

然后,定义一个linklist.cpp文件,是链表方法的实现。如下所示:



  1. //linklist.cpp:链表方法的实现。





  2. #include “stdafx.h”





  3. #include <iostream>





  4. #include “linklist.h”





  5. using




    namespace


    std;




  6. //构造函数




  7. LinkList::LinkList()

  8. {

  9. head = NULL;

  10. length = 0;

  11. }



  12. //析构函数




  13. LinkList::~LinkList()

  14. {

  15. Node *temp;


  16. for


    (


    int


    i=0;i<length;i++)


  17. {

  18. temp=head;

  19. head=head->next;


  20. delete


    temp;


  21. }

  22. }



  23. //得到链表长度





  24. int


    LinkList::Length()


  25. {


  26. return


    length;


  27. }



  28. //在链表头部插入结点





  29. void


    LinkList::InsertHead(Info val)


  30. {

  31. Insert(val,0);

  32. }



  33. //插入结点





  34. void


    LinkList::Insert(Info val,


    int


    pos)


  35. {


  36. if


    (pos<0)


  37. {

  38. cout<<

    “pos must be greater than zero”


    <<endl;



  39. return


    ;


  40. }


  41. int


    index = 1;


  42. Node *temp = head;

  43. Node *node =

    new


    Node(val);



  44. if


    (pos == 0)


  45. {

  46. node->next = temp;

  47. head = node;

  48. length++;


  49. return


    ;


  50. }


  51. while


    (temp!=NULL && index<pos)


  52. {

  53. temp=temp->next;

  54. index++;

  55. }


  56. if


    (temp == NULL)


  57. {

  58. cout<<

    “Insert failed”


    <<endl;



  59. return


    ;


  60. }

  61. node->next = temp->next;

  62. temp->next = node;

  63. length++;

  64. }



  65. //删除结点





  66. void


    LinkList::Remove(Info val)


  67. {


  68. int


    pos = Find(val);



  69. if


    (pos == -1)


  70. {

  71. cout<<

    “Delete failed”


    <<endl;



  72. return


    ;


  73. }


  74. if


    (pos == 1)


  75. {

  76. head = head->next;

  77. length–;


  78. return


    ;


  79. }


  80. int


    index = 2;


  81. Node *temp = head;


  82. while


    (index < pos)


  83. temp = temp->next;

  84. temp->next = temp->next->next;

  85. length–;

  86. }



  87. //查找结点位置





  88. int


    LinkList::Find(Info val)


  89. {

  90. Node *temp = head;


  91. int


    index = 1;



  92. while


    (temp!=NULL)


  93. {


  94. if


    (temp->val.name == val.name && temp->val.id == val.id)



  95. return


    index;


  96. temp = temp->next;

  97. index ++;

  98. }


  99. return


    -1;


    //不存在返回-1




  100. }



  101. //链表反序





  102. void


    LinkList::Reverse()


  103. {


  104. if


    (head==NULL)



  105. return


    ;


  106. Node *curNode=head,*nextNode=head->next,*temp;


  107. while


    (nextNode!=NULL)


  108. {

  109. temp=nextNode->next;

  110. nextNode->next=curNode;

  111. curNode=nextNode;

  112. nextNode=temp;

  113. }

  114. head->next=NULL;

  115. head=curNode;

  116. }



  117. //打印链表





  118. void


    LinkList::Print()


  119. {


  120. if


    (head == NULL)


  121. {

  122. cout<<

    “LinkList is empty”


    <<endl;



  123. return


    ;


  124. }

  125. Node *temp = head;


  126. while


    (temp!=NULL)


  127. {

  128. cout<<temp->val.name<<

    “,”


    <<temp->val.id<<endl;


  129. temp=temp->next;

  130. }

  131. cout<<endl;

  132. }

最后,定义一个main.cpp,用来测试链表功能,如下所示:



  1. // main.cpp : 测试链表功能。





  2. #include “stdafx.h”





  3. #include <iostream>





  4. #include <string>





  5. #include “linklist.h”





  6. using




    namespace


    std;




  7. int


    _tmain(


    int


    argc, _TCHAR* argv[])


  8. {

  9. LinkList head;

  10. Info val1,val2,val3,val4;

  11. val1.id =1,val1.name=

    “Kevin”


    ,val2.id=2,val2.name=


    “Cathy”


    ,val3.id=3,val3.name=


    “Lucy”


    ,val4.id=4,val4.name=


    “Gravin”


    ;




  12. //测试插入功能




  13. cout<<

    “Insert test:”


    <<endl;


  14. head.InsertHead(val1);

  15. head.Print();

  16. head.Insert(val2,1);

  17. head.Print();

  18. head.Insert(val3,4);

  19. head.Print();

  20. head.InsertHead(val3);

  21. head.Insert(val4,2);

  22. head.Print();



  23. //测试反序功能




  24. cout<<

    “reverse test:”


    <<endl;


  25. head.Reverse();

  26. cout<<

    “reversed linklist is:”


    <<endl;


  27. head.Print();



  28. //测试删除功能




  29. cout<<

    “remove test:”


    <<endl;


  30. cout<<

    “the length of linklist is:”


    <<endl;


  31. cout<<head.Length()<<endl;

  32. head.Remove(val4);

  33. head.Print();

  34. cout<<

    “the length of linklist is:”


    <<endl;


  35. cout<<head.Length()<<endl;

  36. head.Remove(val4);

  37. head.Print();


  38. return


    0;


  39. }

测试结果如下图:



转自

http://www.xuebuyuan.com/1389026.html


在看内核v4l2示例


代码


driver/media/video/vivi.c时 ,看到list_add_tail()函数,现在对其进行分析:



  1. <span style=


    “font-size:24px;”


    >


    struct


    list_head {



  2. struct


    list_head *next, *prev;


  3. };


  4. list_add_tail(&buf->vb.queue, &vid->active);


  5. /**



  6. * list_add_tail – add a new entry



  7. * @new: new entry to be added



  8. * @head: list head to add it before



  9. *



  10. * Insert a new entry before the specified head.



  11. * This is useful for implementing queues.



  12. */





  13. static


    <span style=


    “color:#3333ff;”


    >__inline__</span>


    void


    list_add_tail(


    struct


    list_head *_new,


    struct


    list_head *head)


  14. {

  15. <span style=

    “color:#3333ff;”


    >__list_add(_new, head->prev, head);</span>


  16. }



  17. /*



  18. * Insert a new entry between two known consecutive entries.



  19. *



  20. * This is only for internal list manipulation where we know



  21. * the prev/next entries already!



  22. */





  23. static


    __inline__


    void


    __list_add(


    struct


    list_head * _new,



  24. struct


    list_head * prev,



  25. struct


    list_head * next)


  26. {

  27. <span style=

    “color:#3333ff;”


    > next->prev = _new;


  28. _new->next = next;

  29. _new->prev = prev;

  30. prev->next = _new;</span>

  31. }

  32. </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。