目录
上一篇《手写链表C++》
一、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 <- 4
Node* 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 <- 4
Node* 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;
}