• 【原】手写链表(C++)


    • 1、链表基本概念以及注意事项
    •       a、构造函数与析构函数
    •       b、插入
    •       c、重载运算符[]
    • 2、打印链表
    • 3、删除链表节点
    • 4、链表中倒数第k个节点
    • 5、反转链表
    • 6、合并两个排序的链表
    • 7、两个链表当中的第一个公共节点

    【查看之前笔记】

     在编写函数之前,请务必注意以下三点:

    i、输入的指针、各类容器可能是空的

    ii、输入的容器可能只有一个元素

    iii、输入的容器可能有多个元素,这个是最常见的情况。

    【做完下面代码后,请你再按照上面三条,将异常情况加上!!!】

      1 #include<iostream>
      2 #include<set>
      3 using namespace std;
      4 
      5 class Node
      6 {
      7 public:
      8     int data_;//数据阈
      9     Node* next_;//指针阈
     10 public:
     11     Node():data_(-1), next_(nullptr) {}
     12 };
     13 
     14 class List
     15 {
     16 public:
     17     List()
     18     {
     19         this->head_ = new Node();// 不分配空间,下面赋值是不合理的!
     20                                  //this->head_->data_ = 0;//多余?
     21         this->head_->next_ = nullptr;
     22         this->size_ = 0;
     23     };
     24     void insert(int pos, int value);
     25     void remove(int pos);
     26     int get_reverse_element(int reverse_pos);//链表中倒数第k个节点
     27     void reverse();
     28 
     29     int operator[](int i);
     30     void print();
     31     ~List();
     32 
     33 
     34 public:
     35     Node* head_;
     36     int size_;//维护一个size
     37 };
     38 //在第pos个元素前一个位置插入(创建、找到位置、入链表)
     39 void List::insert(int pos, int value)
     40 {
     41     if (pos < 0 || pos > size_)
     42         return;
     43 
     44     //创建新的节点接受数据
     45     Node* newnode = new Node();
     46     newnode->data_ = value;
     47     //cout << "newnode->data_ = " << *newnode->data_ << endl;
     48     newnode->next_ = nullptr;
     49 
     50     //利用辅助指针找到pos前一个节点
     51     // 其实这里不断next,无非就是希望p_curr = nullptr
     52     // 然后56行 让newnode->next_  = nullptr(这个nullptr是从head_->next 传过来的);也就是尾部插入嘛
     53     // 而循环链表 同理 让newnode->next_  = &(head_)(这个 &(head_) 是从head_->next 传过来的);
     54     Node* p_curr = head_;
     55     for (int i = 0; i < pos; i++) //这个for循环本质上是head_->next_->next_......
     56     {
     57         p_curr = p_curr->next_;
     58     }
     59     //现在p_curr就是pos前一个节点的指针阈
     60     //新节点入链表
     61     newnode->next_ = p_curr->next_;//右边
     62     p_curr->next_ = newnode;//左边
     63     size_++;
     64 }
     65 
     66 void List::remove(int pos)
     67 {
     68     if (pos < 0 || pos > size_)
     69     {
     70         return;
     71     }
     72     Node* p_curr = head_;
     73     for (int i = 0; i < pos; i++)// 3
     74     {
     75         p_curr = p_curr->next_;
     76     }
     77     p_curr->next_ = p_curr->next_->next_;
     78     size_--;
     79 }
     80 
     81 //链表中倒数第k个节点
     82 int List::get_reverse_element(int reverse_pos)
     83 {
     84     int pos = size_ - reverse_pos;
     85     Node* p_curr = head_;
     86     for (int i = 0; i < pos; i++)
     87     {
     88         p_curr = p_curr->next_;
     89     }
     90     return p_curr->data_;
     91 }
     92 
     93 //反转链表
     94 void List::reverse()
     95 {
     96     // head   -> 1 -> 2 -> 3 -> 4 -> nullptr
     97     //nullptr <- 1 <- 2 <- 3 <- 4
     98 
     99     Node* p_curr = head_->next_;
    100     Node* p_prev = nullptr;
    101     while (p_curr != nullptr)
    102     {
    103         Node* p_next = p_curr->next_;
    104         if (p_next == nullptr)
    105         if(p_curr->next_ == nullptr)
    106         {
    107             head_->next_ = p_curr;
    108         }
    109         p_curr->next_ = p_prev;
    110         p_prev = p_curr;
    111         p_curr = p_next;
    112     }
    113 }
    114 
    115 int List::operator[](int i)
    116 {
    117     Node* p_curr = head_;
    118     int count = 0;
    119     while (count <= i)
    120     {
    121         p_curr = p_curr->next_;
    122         count++;
    123     }
    124     return p_curr->data_;
    125 }
    126 void List::print()
    127 {
    128     if (size_ == 0)
    129     {
    130         cout << "size = 0" << endl;
    131         return;
    132     }
    133     //遍历
    134     Node* p_curr = head_->next_;//【注意这里next】
    135     while (p_curr != nullptr)
    136     {
    137         cout << p_curr->data_ << " ";
    138         p_curr = p_curr->next_;
    139     }
    140     cout << endl;
    141 }
    142 List::~List()
    143 {
    144     while (size_ != 0)
    145     {
    146         Node* p_curr = head_;
    147         for (int i = 0; i < (size_ - 1); i++)// 012345 i < 5
    148         {
    149             p_curr = p_curr->next_;//for循环执行完,p_curr指向4
    150         }
    151         delete p_curr->next_;//删除最后一个元素
    152         p_curr->next_ = nullptr;//末尾元素 空指针
    153         size_--;
    154         print();
    155     }
    156     delete head_; //【这个容易忘记!】
    157     cout << "delete!" << endl;
    158 }
    159 
    160 //合并两个排序链表
    161 void mergeLists(List& list3, List& list4, List& list34)
    162 {
    163     Node* p_curr3 = list3.head_->next_;
    164     Node* p_curr4 = list4.head_->next_;
    165     Node* p_curr34 = list34.head_->next_;
    166     int location = 0;
    167     while ((p_curr3 != nullptr) || (p_curr4 != nullptr))
    168     {
    169         if ((p_curr3 != nullptr) && (p_curr4 != nullptr))
    170         {
    171             if (p_curr3->data_ < p_curr4->data_)
    172             {
    173                 list34.insert(location, p_curr3->data_);
    174                 location++;
    175                 list34.insert(location, p_curr4->data_);
    176                 location++;
    177             }
    178             else
    179             {
    180                 list34.insert(location, p_curr4->data_);
    181                 location++;
    182                 list34.insert(location, p_curr3->data_);
    183                 location++;
    184             }
    185             p_curr3 = p_curr3->next_;
    186             p_curr4 = p_curr4->next_;
    187         }
    188         else if ((p_curr3 != nullptr) && (p_curr4 == nullptr))
    189         {
    190             list34.insert(location, p_curr3->data_);
    191             location++;
    192             p_curr3 = p_curr3->next_;
    193         }
    194         else if ((p_curr3 == nullptr) && (p_curr4 != nullptr))
    195         {
    196             list34.insert(location, p_curr4->data_);
    197             location++;
    198             p_curr4 = p_curr4->next_;
    199         }
    200     }
    201 }
    202 
    203 
    204 int main()
    205 {
    206     List list1;
    207     //插入
    208     for (int i = 0; i < 15; i++)
    209     {
    210         list1.insert(i, i);
    211     }
    212 
    213     //删除
    214     list1.remove(10);
    215     list1.remove(5);
    216     //打印
    217     list1.print();
    218     list1.reverse();
    219     list1.print();
    220     //访问倒数元素
    221     for (int i = 1; i < 4; i++)
    222     {
    223         cout << "倒数第" << i << "个元素是:" << list1.get_reverse_element(i) << endl;
    224     }
    225     list1.insert(2, 9999);
    226     //重载符[]
    227     for (int i = list1.size_ - 1; i >= 0; i--)
    228     {
    229         cout << list1[i] << " ";
    230     }
    231     cout << endl;
    232     List list2;
    233     list2.insert(0, 10);
    234     list2.insert(1, 20);
    235     list2.insert(2, 30);
    236     list2.print();
    237     int size2 = list2.size_;
    238 
    239     //合并两个排序链表
    240     List list3, list4;
    241     for (int i = 0; i < 5; i++)
    242     {
    243         list3.insert(i, 2 * i);
    244         list4.insert(i, 2 * i + 1);
    245     }
    246     list4.insert(5, 12);
    247     list4.insert(6, 21);
    248     list3.print();
    249     list4.print();
    250 
    251     List list34;
    252     mergeLists(list3, list4, list34);
    253     list34.print();
    254 
    255 
    256     return 1;
    257 }
    View Code

      a、构造函数与析构函数

    节点 ,初始化数据一定要做。

    1 class Node
    2 {
    3 public:
    4     int data_;//数据阈
    5     Node* next_;//指针阈
    6 public:
    7     Node():data_(-1), next_(nullptr)
    8     {}
    9 };

    构造函数

    1 List()
    2 {
    3     this->head_ = new Node();// 分配空间
    4     this->size_ = 0;
    5 };

    整个链表,实现任何功能,我们都要维护一个头节点和size_

    析构函数

     1 List::~List()
     2 {
     3     while (size_ != 0)
     4     {
     5         Node* p_curr = head_;
     6         for (int i = 0; i < (size_ - 1); i++)// 012345 i < 5
     7         {
     8             p_curr = p_curr->next_;//for循环执行完,p_curr指向4
     9         }
    10         delete p_curr->next_;//删除最后一个元素
    11         p_curr->next_ = nullptr;//末尾元素 空指针
    12         size_--;
    13         print();
    14     }
    15     delete head_; //
    16     cout << "delete!" << endl;
    17 }

    在保证不断链的前提下;释放整个链表,似乎只能从后面开始delete。循环当中,每次遍历到 待删除节点前一个p_curr, 然后delete p_curr->next_

    b、插入 

     1 //在第pos个元素前一个位置插入(创建、找到位置、入链表)
     2 void List::insert(int pos, int value)
     3 {
     4     if (pos < 0 || pos > size_)
     5         return;
     6 
     7     //创建新的节点接受数据
     8     Node* newnode = new Node();
     9     newnode->data_ = value;
    10 
    11     Node* p_curr = head_;
    12     for (int i = 0; i < pos; i++) 
    13     {
    14         p_curr = p_curr->next_;
    15     }
    16 
    17     //新节点入链表
    18     newnode->next_ = p_curr->next_;
    19     p_curr->next_ = newnode;
    20     size_++;
    21 }

    18行、19行代码很好诠释了插入过程;其中18行代码:将当前节点的指针域指向 对象的地址 赋给 newnode->next_;保证了新数据与链表中后面连接;

    19行代码:当前节点指针域指向 新的节点

    例如: 1 2 3 4 5  要在 4 和 5 之间插入 一个 666

    步骤如下:

    遍历至节点4;    1 2 3 4 5

    将666 指向5:; 1 2 3 4      666 5

    再将 4 指向 666; 1 2 3 4 666 5

    c、重载运算符

     1 int List::operator[](int i)
     2 {
     3     Node* p_curr = head_;
     4     int count = 0;
     5     while (count <= i)
     6     {
     7         p_curr = p_curr->next_;
     8         count++;
     9     }
    10     return p_curr->data_;
    11 }

    在做上面几道题之前,我们先给出一个基本链表。

    总代码,顺便解释构造函数、析构函数为什么这样写

    2、打印链表

     1 void List::print()
     2 {
     3     if (size_ == 0)
     4     {
     5         cout << "size = 0" << endl;
     6         return;
     7     }
     8     //遍历
     9     Node* p_curr = head_->next_;//【注意这里next】
    10     while (p_curr != nullptr)
    11     {
    12         cout << p_curr->data_ << " ";
    13         p_curr = p_curr->next_;
    14     }
    15     cout << endl;
    16 }

    3、删除链表节点

     1 //功能:删除索引位置为pos的节点
     2 void List::remove(int pos)
     3 {
     4     if (pos < 0 || pos > size_) 
     5     {
     6         return;
     7     }
     8     Node* p_curr = head_;
     9     for (int i = 0; i < pos; i++)// 3
    10     {
    11         p_curr = p_curr->next_;
    12     }
    13     p_curr->next_ = p_curr->next_->next_;
    14     size_--;
    15 }

    思想就是找到要删除的Node的前一个节点,让前一个节点的指针指向Node的下一个节点就行了。

    例如:pos = 3的时候,for循环执行完毕,p_curr表示索引值为2的节点地址,接着我们让p_curr->next 指向 下一个节点的下一个节点。

     4、链表中倒数第k个节点

     1 //链表中倒数第k个节点
     2 int List::get_reverse_element(int reverse_pos)
     3 {
     4     int pos = size_ - reverse_pos;
     5     Node* p_curr = head_;
     6     for (int i = 0; i < pos; i++)
     7     {
     8         p_curr = p_curr->next_;
     9     }
    10     return p_curr->data_;
    11 }

    你可以去看看剑指offer上的做法,我这里类List维护了一个size_,所以比较简单。

    5、反转链表

    修改前:

     1 //反转链表
     2 void List::reverse()
     3 {
     4     // head   -> 1 -> 2 -> 3 -> 4 -> nullptr
     5     //nullptr <- 1 <- 2 <- 3 <- 4
     6 
     7     Node* p_curr = head_;
     8     Node* p_prev = nullptr;
     9     while (p_curr != nullptr)
    10     {
    11         Node* p_next = p_curr->next_;
    12         if (p_next == nullptr)
    13         {
    14             head_ = p_curr;
    15         }
    16         p_curr->next_ = p_prev;
    17         p_prev = p_curr;
    18         p_curr = p_next;
    19     }
    20 }

    可以看到,有一个元素错位了。由于第7行 p_curr一开始 = 头节点指针,执行到第16行的时候,可以看到头节点的下一个节点是nullptr,但是头节点本身 data_ = -1;  当循环执行到if语句的时候,你又把第一个节点直接赋值给头节点,那么头节点的data_ = 15;这个在类List的print()中是不会打印出来的。

    第7、14行做了如下修改:

     1 //反转链表
     2 void List::reverse()
     3 {
     4     // head   -> 1 -> 2 -> 3 -> 4 -> nullptr
     5     //nullptr <- 1 <- 2 <- 3 <- 4
     6 
     7     Node* p_curr = head_->next_;
     8     Node* p_prev = nullptr;
     9     while (p_curr != nullptr)
    10     {
    11         Node* p_next = p_curr->next_;
    12         if (p_next == nullptr)
    13         {
    14             head_->next_ = p_curr;
    15         }
    16         p_curr->next_ = p_prev;
    17         p_prev = p_curr;
    18         p_curr = p_next;
    19     }
    20 }

     思考:

    当初写出来的链表应当是这样的:

     1 //反转链表
     2 void List::reverse()
     3 {
     4     // head   -> 1 -> 2 -> 3 -> 4 -> nullptr
     5     //nullptr <- 1 <- 2 <- 3 <- 4
     6 
     7     Node* p_curr = head_->next_;
     8     Node* p_prev = nullptr;
     9     while (p_curr != nullptr)
    10     {
    11         //Node* p_next = p_curr->next_;
    12         //if (p_next == nullptr)
    13         if(p_curr->next_ == nullptr)
    14         {
    15             head_->next_ = p_curr;
    16         }
    17         p_curr->next_ = p_prev;
    18         p_prev = p_curr;
    19         //p_curr = p_next;
    20         p_curr = p_curr->next_;
    21     }
    22 }

    输出结果:

    首先,必须知道算法第17行是核心!

    这里显然断链了。17行 p_curr->next_ 的赋值导致20行(为了遍历原链表)赋值错误。原本,在17行,我们的目的是:将next_指向前一个节点,以达到倒序的目的。所以在执行17行之前,我们必须想办法缓存地址值:p_curr->next_,也就是11行,缓存之后用于19行刷新p_curr。18行的刷新很好理解。

    6、合并两个排序链表

     1 //合并两个排序链表
     2 void mergeLists(List& list3, List& list4, List& list34)
     3 {
     4     Node* p_curr3 = list3.head_->next_;
     5     Node* p_curr4 = list4.head_->next_;
     6     Node* p_curr34 = list34.head_->next_;
     7     int location = 0;
     8     while ((p_curr3 != nullptr) || (p_curr4 != nullptr))
     9     {
    10         if ((p_curr3 != nullptr) && (p_curr4 != nullptr))
    11         {
    12             if (p_curr3->data_ < p_curr4->data_)
    13             {
    14                 list34.insert(location, p_curr3->data_);
    15                 location++;
    16                 list34.insert(location, p_curr4->data_);
    17                 location++;
    18             }
    19             else
    20             {
    21                 list34.insert(location, p_curr4->data_);
    22                 location++;
    23                 list34.insert(location, p_curr3->data_);
    24                 location++;
    25             }
    26             p_curr3 = p_curr3->next_;
    27             p_curr4 = p_curr4->next_;
    28         }
    29         else if ((p_curr3 != nullptr) && (p_curr4 == nullptr))
    30         {
    31             list34.insert(location, p_curr3->data_);
    32             location++;
    33             p_curr3 = p_curr3->next_;
    34         }
    35         else if ((p_curr3 == nullptr) && (p_curr4 != nullptr))
    36         {
    37             list34.insert(location, p_curr4->data_);
    38             location++;
    39             p_curr4 = p_curr4->next_;
    40         }
    41     }
    42 }

    例如现在有两个升序序列:

    A:0 2 4 6 8 14                                                                                                       

    B:1 3 5 7 9 12 21 31 

    要将他们变成一个生序序列;

    思路:假设现在两个序列元素个数相等;我们将 0 1对比将得到 0 1, 再将1和2 3 对比 得到 0 1 2 3;再将3和4 5 对比;依次类推,(对应第10行代码)

    现在考虑 A序列长度 > B序列长度;对应第29行代码; A序列长度 < B序列长度;对应第35行代码

     1     //合并两个排序链表
     2     List list3,list4;
     3     for (int i = 0; i < 5; i++)
     4     {
     5         list3.insert(i, 2*i);
     6         list4.insert(i, 2 * i + 1);
     7     }
     8     list3.insert(5, 14);
     9     list4.insert(5, 12);
    10     list4.insert(6, 21);
    11     list4.insert(7, 31);
    12     list3.print();
    13     list4.print();
    14 
    15     List list34;
    16     mergeLists(list3, list4, list34);
    17     list34.print();

    测试用例:

  • 相关阅读:
    jqyery dataTable 基本用法
    linux 下两台电脑之间ssh无密码连接
    javascript 高级程序设计 十二
    javascript 高级程序设计 十一
    javascript 高级程序设计 十
    小程序笔记
    表单提交 fastadmin form
    fastadmin表单验证
    sbadmin表单事件
    fastadmin模态框(弹出框)
  • 原文地址:https://www.cnblogs.com/winslam/p/10089004.html
Copyright © 2020-2023  润新知