目录
一、手写List成员方法
1.1 打印链表
1.2 删除链表节点
1.3 链表中倒数第k个节点
1.4 反转链表
1.5 合并两个排序链表
二、完整代码
一、C++实现链表成员方法
在上一篇博客《手写链表C++》,实现了基本的List类。在面试中,经常被问到List如何反转、删除元素等,同时也为了丰富List类的成员;这一节本文实现如题等list操作。
C++链表,一种重要的数据结构,由一系列节点构成,每个节点包含两部分:数据和指向下一个节点的指针。链表是一种物理存储单元上非连续、非顺序的存储结构,数据结构的逻辑顺序是通过链表中的指针链接次序实现的。链表的最简单形式是单向链表,它只包含一个信息域和一个指针域。链表的优点是可以动态地分配内存空间,实现高效的数据操作。在C++中,链表的每个节点都是通过指针链接在一起,从而形成一个连续的链式结构。
1.1 打印链表
为了方便debug代码,先写一个打印链表的函数:
void List::print()
{if (size_ == 0){cout << "size = 0" << endl;return;}//遍历Node* p_curr = head_->next_;//【注意这里next】while (p_curr != nullptr){cout << p_curr->data_ << " ";p_curr = p_curr->next_;}cout << endl;
}
1.2 删除链表节点
思想就是找到要删除的Node的前一个节点,让前一个节点的指针指向Node的下一个节点就行了。
例如:pos = 3的时候,for循环执行完毕,p_curr表示索引值为2的节点地址,接着我们让p_curr->next 指向 下一个节点的下一个节点。
//功能:删除索引位置为pos的节点
void List::remove(int pos)
{if (pos < 0 || pos > size_){return;}Node* p_curr = head_;for (int i = 0; i < pos; i++)// 3{p_curr = p_curr->next_;}p_curr->next_ = p_curr->next_->next_;size_--;
}
1.3 链表中倒数第k个节点
你可以去看看剑指offer上的做法,我这里类List维护了一个size_,所以比较简单。
//链表中倒数第k个节点
int List::get_reverse_element(int reverse_pos)
{int pos = size_ - reverse_pos;Node* p_curr = head_;for (int i = 0; i < pos; i++){p_curr = p_curr->next_;}return p_curr->data_;
}
1.4 反转链表
这个方法是必学的,因为面试中经常问,甚至需要现场手撕。
//反转链表
void List::reverse()
{// head -> 1 -> 2 -> 3 -> 4 -> nullptr//nullptr <- 1 <- 2 <- 3 <- 4Node* p_curr = head_->next_;Node* p_prev = nullptr;while (p_curr != nullptr){Node* p_next = p_curr->next_;if (p_next == nullptr){head_->next_ = p_curr;}p_curr->next_ = p_prev;p_prev = p_curr;p_curr = p_next;}
}
下图是反转效果:
1.5 合并两个排序链表
//合并两个排序链表
void mergeLists(List& list3, List& list4, List& list34)
{Node* p_curr3 = list3.head_->next_;Node* p_curr4 = list4.head_->next_;Node* p_curr34 = list34.head_->next_;int location = 0;while ((p_curr3 != nullptr) || (p_curr4 != nullptr)){if ((p_curr3 != nullptr) && (p_curr4 != nullptr)){if (p_curr3->data_ < p_curr4->data_){list34.insert(location, p_curr3->data_);location++;list34.insert(location, p_curr4->data_);location++;}else{list34.insert(location, p_curr4->data_);location++;list34.insert(location, p_curr3->data_);location++;}p_curr3 = p_curr3->next_;p_curr4 = p_curr4->next_;}else if ((p_curr3 != nullptr) && (p_curr4 == nullptr)){list34.insert(location, p_curr3->data_);location++;p_curr3 = p_curr3->next_;}else if ((p_curr3 == nullptr) && (p_curr4 != nullptr)){list34.insert(location, p_curr4->data_);location++;p_curr4 = p_curr4->next_;}}
}
例如现在有两个升序序列:
- 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行代码。
//合并两个排序链表List list3,list4;for (int i = 0; i < 5; i++){list3.insert(i, 2*i);list4.insert(i, 2 * i + 1);}list3.insert(5, 14);list4.insert(5, 12);list4.insert(6, 21);list4.insert(7, 31);list3.print();list4.print();List list34;mergeLists(list3, list4, list34);list34.print();
测试用例:
二、链表完整代码
以下代码包含:List的实现,节点定义,链表的插入、删除、合并、反转等。
#include<iostream>
using namespace std;class Node
{
public:int data_;//数据阈Node* next_;//指针阈
public:Node() :data_(-1), next_(nullptr) {}
};class List
{
public:List(){this->head_ = new Node();// 不分配空间,下面赋值是不合理的!//this->head_->data_ = 0;//多余?this->head_->next_ = nullptr;this->size_ = 0;};void insert(int pos, int value);void remove(int pos);int get_reverse_element(int reverse_pos);//链表中倒数第k个节点void reverse();int operator[](int i);void print();~List();
public:Node* head_;int size_;//维护一个size
};
//在第pos个元素前一个位置插入(创建、找到位置、入链表)
void List::insert(int pos, int value)
{if (pos < 0 || pos > size_)return;//创建新的节点接受数据Node* newnode = new Node();newnode->data_ = value;//cout << "newnode->data_ = " << *newnode->data_ << endl;newnode->next_ = nullptr;//利用辅助指针找到pos前一个节点// 其实这里不断next,无非就是希望p_curr = nullptr// 然后56行 让newnode->next_ = nullptr(这个nullptr是从head_->next 传过来的);也就是尾部插入嘛// 而循环链表 同理 让newnode->next_ = &(head_)(这个 &(head_) 是从head_->next 传过来的);Node* p_curr = head_;for (int i = 0; i < pos; i++) //这个for循环本质上是head_->next_->next_......{p_curr = p_curr->next_;}//现在p_curr就是pos前一个节点的指针阈//新节点入链表newnode->next_ = p_curr->next_;//右边p_curr->next_ = newnode;//左边size_++;
}void List::remove(int pos)
{if (pos < 0 || pos > size_){return;}Node* p_curr = head_;for (int i = 0; i < pos; i++)// 3{p_curr = p_curr->next_;}p_curr->next_ = p_curr->next_->next_;size_--;
}//链表中倒数第k个节点
int List::get_reverse_element(int reverse_pos)
{int pos = size_ - reverse_pos;Node* p_curr = head_;for (int i = 0; i < pos; i++){p_curr = p_curr->next_;}return p_curr->data_;
}//反转链表
void List::reverse()
{// head -> 1 -> 2 -> 3 -> 4 -> nullptr//nullptr <- 1 <- 2 <- 3 <- 4Node* p_curr = head_->next_;Node* p_prev = nullptr;while (p_curr != nullptr){Node* p_next = p_curr->next_;if (p_next == nullptr)if (p_curr->next_ == nullptr){head_->next_ = p_curr;}p_curr->next_ = p_prev;p_prev = p_curr;p_curr = p_next;}
}int List::operator[](int i)
{Node* p_curr = head_;int count = 0;while (count <= i){p_curr = p_curr->next_;count++;}return p_curr->data_;
}
void List::print()
{if (size_ == 0){cout << "size = 0" << endl;return;}//遍历Node* p_curr = head_->next_;//【注意这里next】while (p_curr != nullptr){cout << p_curr->data_ << " ";p_curr = p_curr->next_;}cout << endl;
}
List::~List()
{while (size_ != 0){Node* p_curr = head_;for (int i = 0; i < (size_ - 1); i++)// 012345 i < 5{p_curr = p_curr->next_;//for循环执行完,p_curr指向4}delete p_curr->next_;//删除最后一个元素p_curr->next_ = nullptr;//末尾元素 空指针size_--;print();}delete head_; //【这个容易忘记!】cout << "delete!" << endl;
}//合并两个排序链表
void mergeLists(List& list3, List& list4, List& list34)
{Node* p_curr3 = list3.head_->next_;Node* p_curr4 = list4.head_->next_;Node* p_curr34 = list34.head_->next_;int location = 0;while ((p_curr3 != nullptr) || (p_curr4 != nullptr)){if ((p_curr3 != nullptr) && (p_curr4 != nullptr)){if (p_curr3->data_ < p_curr4->data_){list34.insert(location, p_curr3->data_);location++;list34.insert(location, p_curr4->data_);location++;}else{list34.insert(location, p_curr4->data_);location++;list34.insert(location, p_curr3->data_);location++;}p_curr3 = p_curr3->next_;p_curr4 = p_curr4->next_;}else if ((p_curr3 != nullptr) && (p_curr4 == nullptr)){list34.insert(location, p_curr3->data_);location++;p_curr3 = p_curr3->next_;}else if ((p_curr3 == nullptr) && (p_curr4 != nullptr)){list34.insert(location, p_curr4->data_);location++;p_curr4 = p_curr4->next_;}}
}int main()
{List list1;//插入for (int i = 0; i < 15; i++){list1.insert(i, i);}//删除list1.remove(10);list1.remove(5);//打印list1.print();list1.reverse();list1.print();//访问倒数元素for (int i = 1; i < 4; i++){cout << "倒数第" << i << "个元素是:" << list1.get_reverse_element(i) << endl;}list1.insert(2, 9999);//重载符[]for (int i = list1.size_ - 1; i >= 0; i--){cout << list1[i] << " ";}cout << endl;List list2;list2.insert(0, 10);list2.insert(1, 20);list2.insert(2, 30);list2.print();int size2 = list2.size_;//合并两个排序链表List list3, list4;for (int i = 0; i < 5; i++){list3.insert(i, 2 * i);list4.insert(i, 2 * i + 1);}list4.insert(5, 12);list4.insert(6, 21);list3.print();list4.print();List list34;mergeLists(list3, list4, list34);list34.print();return 1;
}