zoukankan      html  css  js  c++  java
  • 3.1 单链表

     

    1.单链表简介

       数组在编程语言中用的非常有用,但是属数组至少有两个缺点
     (1) 编译时就得知道数组的大小
     (2) 在计算机内存中是连续存储的,这就意味着在数组中插入或删除一个数据,就需要调整其他数据。
    而使用链表结构就不存在这些问题
    下图表示一个链表的结构及其构造过程。
     
    上图所示的链表中的每个节点都是下面类定义(节点值为整数型)的一个实例:
    这是一种C++面向对象的写法,在C语言中,我们习惯定义成结构体。
    1. classIntSLLNode{
    2. public:
    3. IntSLLNode(){//第一个构造函数初始化next指针为NULL
    4. next =0;
    5. }
    6. IntSLLNode(int i,IntSLLNode*in =0){//第二个构造函数设置两个成员数据的值
    7. info = i; next = in;
    8. }
    9. int info;
    10. IntSLLNode*next;//指向下一个节点,这个节点的类型与本节点相同,这里要定义成公有成员,以便实施链表的操作
    11. };
    节点包含两个数椐成员: info 和 next,  info 成员用于对用户有用的存储, next 是用于连接链表节点对从而形成链表结构。
    有了这个定义我们就可以构造上图所示的链表了:
    IntSLLNode *p = new IntSLLNode(10);
    p->next = new IntSLLNode(8);
    p->next->next = new IntSLLNode(50);
    这里仅定义了一个头指针p来访问数据,不是很方便,在实际操作中一般会定义两个指针或更多指针方便操作。

     

    2.单链表操作

    链表节点类型已经由类IntSLLNode定义好了,接下来就是定义一个类实施对链表的各种错作,单链表的操作无非是增删查改
    定义下面的类IntSLList
    1. classIntSLList{
    2. public:
    3. IntSLList(){
    4. head = tail =0;
    5. }
    6. ~IntSLList();
    7. int isEmpty(){
    8. return head ==0;
    9. }
    10. void addToHead(int);
    11. void addToTail(int);
    12. int deleteFromHead();// delete the head and return its info;
    13. int deleteFromTail();// delete the tail and return its info;
    14. void deleteNode(int);
    15. bool isInList(int)const;
    16. void printAll()const;
    17. private:
    18. IntSLLNode*head,*tail;//两个私有成员变量,分别是指向单链表的头和尾的指针
    19. };

    2.1 插入
    (1)添加到链表开头
    四步:
    • 创建一个空节点
    • 初始化节点值el
    • 添加新节点到链表最前面,并将新节点的next指向头节点,也就是head的当前值
    • 将head更新为指向新节点的指针
    1. voidIntSLList::addToHead(int el){
    2. head =newIntSLLNode(el,head);//一句话就完成了四步,这就是C++的魅力了
    3. if(tail ==0)
    4. tail = head;
    5. }
    (2)添加到链表末尾
    四步:
    • 创建一个空节点
    • 初始化节点值el,因为该节点要添加到链表的末尾,所以将新节点的next设置为NULL
    • 让原链表的尾节点的next指向新建的尾节点,这样就将新节点添加到了末尾
    • 将tail更新为指向新节点的指针
    1. voidIntSLList::addToTail(int el){
    2. if(tail !=0){// 先判断链表是否为空
    3. tail->next =newIntSLLNode(el);
    4. tail = tail->next;
    5. }
    6. else head = tail =newIntSLLNode(el);
    7. }
     
    2.2 删除
    (1)删除头节点并返回它的值
    为了程序的严谨需要考虑两种特殊情况:
    • 一种是试图从一个空链表删除一个节点。
    • 二是链表只有一个节点的情况。
    1. intIntSLList::deleteFromHead(){
    2. if(!isEmpty())//检查链表链表是否为空
    3. {
    4. int el = head->info;
    5. IntSLLNode*tmp = head;
    6. if(head == tail)//加入链表只有一个节点;
    7. head = tail =0;
    8. else head = head->next;
    9. delete tmp;
    10. return el;
    11. }
    12. else 
    13. //std::cout<<"The List is Empty!"
    14. return0;
    15. }
    注意:上面的程序实际上有一个问题,程序开始用if语句检查了链表是否为空,假如为空就就会执行else的操作返回0;而对于调用
    这个方法的语句并不知道返回的0是头节点本身的值是0,还是因为链表为空而返回的0。最好的解决方法是调用此方法之前就判定一
    下链表是否为空。
    (2)删除尾节点并返回它的值
    该删除操作由成员函数 deleteFromTail实现。 问题在删除了尾节点之后, tail应当指向链表的新末尾节点,也就是说, tail 必须反方向移动一个节点,但是因为从最后一个节点到它的前驱节点没有直接的链接,所以无法进行反方向移动。 因此必须从链表的幵头查找这个前驱 节点,并恰好在tail前面停止。这个任务是通过在for在 循环中用个临时变量temp遍历链表完成的 。
    1. intIntSLList::deleteFromTail(){
    2. int el = tail->info;
    3. if(head == tail){// if only one node on the list;
    4. delete head;
    5. head = tail =0;
    6. }
    7. else{// if more than one node in the list,
    8. IntSLLNode*tmp;// find the predecessor of tail;
    9. for(tmp = head; tmp->next != tail; tmp = tmp->next);
    10. delete tail;
    11. tail = tmp;// the predecessor of tail becomes tail;
    12. tail->next =0;
    13. }
    14. return el;
    15. }
    (3) 删除值为el的节点
    前面讨论的这两种刪除操作是从开头或者末尾删除一个节点。如果要刪除一个包含特定整数的节点,而不关心这个节点在链表中的位置就需要使用不同的方法。这个节点也许正好在链表的开头、末尾,或者在链表中的任何位置。简单地说,必须先找到这个节点, 然后将其前驱节点与后继节点连接,从而将其从链表中删除。因为不知道该节点在什么位置, 操作复杂得多。
    将前驱结点以及后继节点连接起来就 能从链表中删除这个节点 ,但是因为链表只有向后的链接,因此无法从某个节点获得其前驱。完成这个忏务的方法之一是先扫描链表找到处要删除的节点,然后再次扫描链表找到它的前驱,另一种方法如下图所示,借助两个指针变量pred和tmp。
    前面的图只讨论了 一种情况,还有下面几种情况:
    • 试图从空链表中刪除节点,此时函数立即退出。
    • 从单节点链表中删除唯一的节点: head 和 tail都设置成 null
    • 从至少有两个节点的链表中刪除第一个节点,此吋需要更新 head
    •  从至少有两个节点的链表中刪除最后一个节点,此吋需要更新 tail
    •  链表中不存在包含某个数字的节点:不进行任何操作.
    1. voidIntSLList::deleteNode(int el){
    2. if(head !=0)  // if non-empty list;
    3. {
    4. if(head == tail && el == head->info){// if only one
    5. delete head;// node on the list;
    6. head = tail =0;
    7. }
    8. elseif(el == head->info){// if more than one node on the list
    9. IntSLLNode*tmp = head;
    10. head = head->next;
    11. delete tmp;// and old head is deleted;
    12. }
    13. else{// if more than one node in the list
    14. IntSLLNode*pred,*tmp;
    15. for(pred = head, tmp = head->next;// and a non-head node
    16. tmp !=0&&!(tmp->info == el);// is deleted;
    17. pred = pred->next, tmp = tmp->next)
    18. if(tmp !=0){
    19. pred->next = tmp->next;
    20. if(tmp == tail)
    21. tail = pred;
    22. delete tmp;
    23. }
    24. }
    25. }
    26. }
    2.3 查找
    插入和删除操作都对链表进行了修改。查找操作扫描已有的链表,以确定其中是否包含某个数, 在此用布尔成员函数 isInList()实现这个操作。
    1. boolIntSLList::isInList(int el)const{
    2. IntSLLNode*tmp;
    3. for(tmp = head; tmp !=0&&!(tmp->info == el); tmp = tmp->next);
    4. return tmp !=0;
    5. }





  • 相关阅读:
    bzoj1691 [Usaco2007 Dec]挑剔的美食家
    cf493D Vasya and Chess
    cf493C Vasya and Basketball
    cf493B Vasya and Wrestling
    cf493A Vasya and Football
    bzoj1106 [POI2007]立方体大作战tet
    bzoj1537 [POI2005]Aut- The Bus
    bzoj1103 [POI2007]大都市meg
    bzoj1935 [Shoi2007]Tree 园丁的烦恼
    poj2299 Ultra-QuickSort
  • 原文地址:https://www.cnblogs.com/star91/p/4761742.html
Copyright © 2011-2022 走看看