本部分知识比较简单。算法题比较基础,并且比较注重代码细节。注意:尽可能的一题多解。
#include
using namespace std;
//定义节点
template <class T>
struct node
{
T data;
node* next;
node() : data(T()), next(nullptr) {}; //T 类型必须含有完成的赋值函数,防止其他错误。
node(T val) : data(val), next(nullptr) {};
node(T val, node* ptr) : data(val), next(ptr) {};
virtual ~node() {};
};
// 别名
using node_nums = unsigned long int;
//template
//using list_node = node;
// 1.注释:
// 注意语法 typedef不能为模板类取别名
// 比如下语句无法通过编译:
/*
* template
* typedef struct node list_node;
*/
// 2.如果强行使用,可以借助包裹一个类的方式实现
/*
* template
* struct list_node {
* typedef struct node node;
* };
*/
//使用时候必须如下使用:
/* list_node::node a;
* cout << a.data;
*/
// 3. 更方便的用法是借助using关键字,如上
// 为节点定义指针(迭代器)以及相关操作,方便使用
template<class T>
struct list_node_ptr {
node<T>* iter;
// 构造函数
list_node_ptr() : iter(nullptr) {};
list_node_ptr(T val) {
iter = new node<T>(val);
}
list_node_ptr(T val, list_node_ptr ptr) {
iter = new node<T>(val, ptr);
}
// 拷贝构造函数
list_node_ptr(const list_node_ptr& x) : iter(x.iter) {}
// 析构函数
virtual ~list_node_ptr() {};
// *iter
T& operator* () const {
return (*iter).data;
}
// 注意 -> 为一元操作运算符,返回指向自身元素的指针
node<T>* operator-> () const {
return &(operator*());
}
// 为了简单方便,重载++表示类似ptr = ptr-> next含义。
// 后置++ (ptr++)
list_node_ptr<T> operator++ (int) {
list_node_ptr<T> temp = *this; // 注意: 这里调用的是拷贝构造,不是*的重载。原因1.编译器首先遇到 “=”, 2. * 对本身元素,不是指向自己的指针
++*this;
return temp;
}
// 前置++ (++ptr)
list_node_ptr<T>& operator++() {
iter = (*iter).next;
return *this;
}
};
//定义单向链表和基本操作
template <class T>
class forward_linklist
{
private:
// 数据段
list_node_ptr<T> head_node; // 头元素指针(迭代器)
node_nums list_len; // 链表长度
// 保证只有一个实例,以防使用时候出现异常错误
forward_linklist() : head_node(), list_len(0) {};
virtual ~forward_linklist() {
forward_linklist_clear();
}
// 禁用拷贝
forward_linklist(const forward_linklist& ptr) = delete;
public:
// 两种初始化方式: 默认和列表初始化
static forward_linklist* get_linklist() {
return new forward_linklist();
}
static forward_linklist* get_linklist(const initializer_list<T>& lst) {
auto ptr = new forward_linklist();
for (const auto& val : lst) {
ptr->add_node(val, ptr->get_len());
}
return ptr;
}
// 查找操作:
list_node_ptr<T>& find_node(node_nums pos) {
auto ptr = head_node;
if (pos >= list_len) {
return list_node_ptr<T>();// 暂时使用空表示查找失败
}
for (int i = 1; i < pos; ++i) {
++ptr;
}
return ptr;
}
// 删除操作:
// 删除第i个元素(下标从0开始计数),返回删除的元素前一个元素指针(迭代器)
// 如果是第一个元素,和失败一样返回nullptr
list_node_ptr<T>& delete_node(node_nums pos) {
if (pos >= list_len) {
return list_node_ptr<T>();// 暂时使用空表示删除失败
}
else if (pos == 0) { // 表示删除第一个元素
auto temp = head_node++;
delete temp.iter;
--list_len;
return list_node_ptr<T>();
}
auto prior_ptr = find_node(pos - 1);
auto del_ptr = prior_ptr;
++del_ptr;
auto next_ptr = del_ptr;
++next_ptr;
delete del_ptr.iter;
prior_ptr.iter->next = next_ptr.iter;
--list_len;
return prior_ptr;
}
// 修改操作:
// 修改第pos个元素,值改为val,返回修改后的元素迭代器
list_node_ptr<T>& update_node(T val, node_nums pos) {
if (pos >= list_len) {
return list_node_ptr<T>(); // 暂时使用空表示修改失败
}
auto ptr = find_node(pos);
*ptr = val;
return ptr;
}
// 插入操作:在第pos个元素后面进行插入元素操作(默认尾端插入)
// 返回插入后的位置迭代器
list_node_ptr<T>& add_node(T val, node_nums pos = list_len) {
if (pos > list_len) {
return list_node_ptr<T>();
} else if (pos == list_len && pos == 0) { // 插入第一个元素
head_node = list_node_ptr<T>(val);
++list_len;
return head_node;
}
auto ptr = list_node_ptr<T>(val);
auto insert_node = head_node;
for (int i = 1; i < pos; ++i) {
++insert_node;
}
auto insert_next_node = insert_node;
++insert_next_node;
insert_node.iter->next = ptr.iter;
ptr.iter->next = insert_next_node.iter;
++list_len;
return ptr;
}
// 清空所有元素
void forward_linklist_clear() {
while (list_len) {
list_node_ptr<T> ptr = head_node++;
delete ptr.iter;
--this->list_len;
}
}
// 返回长度
node_nums get_len() const {
return list_len;
}
// 修改链表长度 (!!!不安全,考虑简单实现没有继续改进)
node_nums& change_len(const node_nums& len){
list_len = len;
}
// 交换
void swap(forward_linklist<T>& lst1) {
auto lst = lst1;
lst1.head_node = this->head_node;
lst1.change_len(this->get_len());
this->head_node = lst.head_node;
this->list_len = lst.get_len();
}
};
测试代码:
// 测试 int类型;
//本来想顺便测试char*(实际C++中建议不要使用裸指针)
// 没时间算了(晕)
// 遍历一次
void print(forward_linklist<int>* head) {
cout << endl;
int len = head->get_len();
cout << "The current length is " << len << endl;
int pos = 0;
auto ptr = head;
while (pos < len) {
cout << *(ptr->find_node(pos++)) << " ";
}
cout << endl;
}
int main()
{
auto list_head1 = forward_linklist<int>::get_linklist();
auto list_head2 = forward_linklist<int>::get_linklist({ 1,2,3,4,5 });
print(list_head1);
print(list_head2);
cout << "find pos 1 elem : " << *list_head2->find_node(1) << endl << endl;
cout << "______________" << endl;
cout << "delete pos 1 elem : " << *list_head2->delete_node(1) << endl;
cout << "______________" << endl;
print(list_head2);
cout << "insert pos 3 elem (100):" << *list_head2->add_node(100, 3) << endl << endl;
print(list_head2);
cout << "______________" << endl;
cout << "update pos 4 elem" << *list_head2->update_node(1000,4)<< endl;
cout << "______________" << endl;
return 0;
}
由于双向链表仅仅是单向链表添加了一个prior指针,许多帖子已经总结得很好了。这里不仔细总结了。可以推荐参考:link1, link2.
这里假设容器存放元素类型为int(由于与vector类似很多,这里不仔细举例)
迭代器
//注意和vector,array差别,他们都是容器迭代器双向随机迭代器,而这个并不是。
//以下操作list不能使用 iter[i];
//iter -=i ,iter += i , iter + i, iter - i;
//iter1 < iter2; iter1 <= iter2; iter1 > iter2; iter1 >= iter2;
//可以使用的有
# include
vector<list> ls{1,2,3,4,5};
auto iter1 = ls.begin()
auto iter2 = ls.end();
iter1++;
++iter1;
--iter1;
iter1--;
*iter;
iter1 == iter2;
iter1 != iter2;
//提供begin(),end();
//rbegin(),rend();
//cbegin(),cend();
//crbrgin(),crend();这些迭代器具体含义和数组篇同理,省略
创建和初始化
//参考vector
list<int> ls1({1,2,3,4,5});
list<int> ls2{4,10,17,30};
list<int> ls3(-10);
list<int> ls4(10 , 5);
list<int> ls5(ls4);
CURD操作
假设2中容器都存在
//容量大小(和vector一样)
// empty, size, max_size,resize
// 查找
front(); back();//查找第一个或最后一个元素引用
//修改
assign();//基本同vector;
assign(n,10);ls2.assign(ls1.begin(),ls1.end());
ls1.assign({-1,-3,-5});
// 插入元素
emplace(),insert();
push_front(),emplace_front(); //与push_back()等想反,在头部插入元素
push_back();emplace_back();//以上函数基本和vector用法一致
splice() //将一个链表该部分插入到另一个链表的指定位置
//删除操作
pop_front(),pop_back();
erase();clear(); //略
remove(val);//删除容器中所有等于 val 的元素
remove_if();//删除容器中满足条件的元素。
unique();//删除容器中相邻的重复元素,只保留一个。
//其他
merge();//合并两个有序链表,保证合并后链表依旧有序
sort();//排序
reverse();//反转容器元素顺序
swap();//交换两个容器中的元素,要求容器以及存储元素类型一致
基本和上面内容一样,具体参考reference部分forward_list。
主要注意:
forward_list迭代器基于list的相比是单向的。不支持‘–’(减减)运算符,同样也不是随机的。
还有部分API不同,如erase_after,insert_after,splice_after
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prior_node = nullptr, *cur_node = head; // 前一个节点,翻转的当前节点
while (cur_node) {
ListNode* next_node = cur_node->next; //翻转后的节点需要临时记录,以便迭代
cur_node->next = prior_node; // 直接翻转
// 移动到下一个
prior_node = cur_node;
cur_node = next_node;
}
return prior_node; // 返回头结点(最后状态是 prior_node - nullptr(cur_node) - nullptr(next_node))
}
};
b.头插法/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (head == nullptr) {
return head;
}
ListNode* dummy = new ListNode(0);// 使用一个辅助头结点进行头插法
while (head) {
ListNode* next_head = head->next; //保存一下下一个待处理结点
head->next = dummy->next; //将当前节点挂上去
dummy->next = head;
head = next_head;
}
head = dummy->next; //指向新的首个节点,释放辅助头结点内存
delete dummy;
return head;
}
};
c.递归 /**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//出发点假设其他的部分已经反转,如何反转其他部分
// 停止递归调用(链表长度为小于等于1时)
if (head == nullptr || head->next == nullptr) {
return head;
}
//递归,找到倒数第1个节点(或者是新的头结点) new_head,当前head节点为倒数第二个
ListNode* new_head = reverseList(head->next);
head->next->next = head; //改变指向 (由于new_head是新的头,这里写new_head->next = head不对, 注意递归函数返回时候处理部分中间情况应该为 .... ->head-> new_tail<-.....new_head)
head->next = nullptr;// head成为新的部分的尾巴
return new_head; //返回改变后的头结点
}
};
三个方法时间复杂度O(n)。
空间复杂度除了最后是O(n),其他两个为O(1)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
ListNode* front_ptr;
public:
bool isPalindrome(ListNode* head) {
front_ptr = head;
return recursively_check(head);
}
bool recursively_check(ListNode* node) {
if (node != nullptr) {
if (!recursively_check(node->next)) { // 开始递归,找到最后一个元素比较后返回值
return false;
}
// 执行过程是先到尾结点后一个,进行判断,后面接着返回
if (node->val != front_ptr->val) {
return false;
} else {
front_ptr = front_ptr->next; // 修改全局对称指针,继续判断
}
}
return true; // 最后空节点是,先预设为true;递归返回时候继续处理
}
};
c. 双指针(快慢指针)/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* find_middle_node(ListNode* head) {
ListNode* slow = head, * fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* reverse_list(ListNode* head) {
ListNode* prev = nullptr, * cur = head;
while(cur) {
ListNode* cur_next = cur->next;
cur->next = prev;
prev = cur;
cur = cur_next;
}
return prev;
}
bool isPalindrome(ListNode* head) {
if (head == nullptr) { //排除空链表
return true;
}
// 找中点或其后面一位
ListNode* middle_node = find_middle_node(head);
// 翻转后面部分
ListNode* new_head = reverse_list(middle_node);
// 进行判断
ListNode* cur1 = new_head;
ListNode* cur2 = head;
bool flag = true;
ListNode* prev_tail = nullptr;
while(cur1 && cur2) {
if (prev_tail == nullptr && (cur1->next == nullptr || cur1->next->next== nullptr)) { // 小优化,后面无需找前半部分最后一个节点
prev_tail = cur1;
}
if (cur1->val != cur2->val) { // 进行比较
flag = false;
} //由于之前优化部分,所以必须要找到中点前一个节点,这里不能比较错误直接跳出循环
cur1 = cur1->next;
cur2 = cur2->next;
}
//还原链表
new_head = reverse_list(new_head);
prev_tail->next = new_head;
return flag;
}
};
三个方法时间复杂度O(n)。
空间复杂度除了第二个是O(n),其他两个为O(1)
其他补充:
重排链表
k个一组翻转链表
从头到尾打印链表
环路检查
思路
a. 哈希:直接记录节点,遇到同样节点直接返回(略)
b.快慢指针:可以看看题解(主要注意计算一下)
核心代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow = head, *fast = head;
// 找到第一次相遇点
while(fast && fast->next) {
slow = slow ->next;
fast = fast ->next->next;
if (slow == fast) { // 找到后,慢指针路程为1份,快指针路程为1份+k个环的长度+一段
fast = head;
while (fast != slow) { // 将快指针重新置于起点,进行一步一步遍历,抵消1份中不是环的长度
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return nullptr;
}
};
时间复杂度:O(n)。
空间复杂度:O(1)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int kthToLast(ListNode* head, int k) {
ListNode* pa = head, *pb = head;
for (int i = 0; i < k && pa; ++i) { // 防止k无效,导致pa越界
pa = pa -> next;
}
while (pa) {
pa = pa->next;
pb = pb->next;
}
return pb->val;
}
};
时间复杂度O(n)。
空间复杂度O(1)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void deleteNode(ListNode* node) {
node -> val = node->next->val;
node->next = node->next->next;
}
};
时间复杂度O(1)。
空间复杂度O(1)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (head == nullptr || head->next == nullptr) { //空节点或一个节点
return head;
}
ListNode* slow = head, *fast = head->next; //初始条件
while (fast) {
if (slow->val == fast->val) {
slow->next = fast->next;
} else {
slow = slow->next;
}
fast = fast->next; // 两句判断语句都要做fast更新,只是对slow的更新不一样
}
return head;
}
};
时间复杂度O(n)。
空间复杂度O(1)。
其他补充:
删除排序链表中的重复元素 II
删除链表的节点
排序链表
思路
a. 插入排序:使用插入排序,注意代码细节
b. 归并排序:这里介绍自底向上排序方法,另一种自顶向下可以参考这里:
由于其他logn的排序不是变化相邻元素(往往需要随机范文),归并排序能够做到只访问相邻元素。
主要思想是模仿归并排序进行code。
难度在于:代码细节较多,控制要求较高。
c.堆排序:考虑堆结构,将所有节点放入堆中,不停重建链表。
核心代码
a.插入排序
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* insertionSortList(ListNode* head) {
if (head == nullptr) {
return head;
}
ListNode* dummy = new ListNode(0 , head); // 头插法进行插入排序
ListNode *prev = nullptr, *cur = head->next; // 排序需要移动元素必须指针
ListNode* last_sort_node = head; // 已经排好序元素分界线
while (cur) {
if (last_sort_node -> val <= cur -> val) {
last_sort_node = last_sort_node -> next; // 扩展有序段,能够保证如果没有调整,last_sort_node->next == cur
} else { //需要进行调整
ListNode* prev = dummy;
while (prev -> next -> val <= cur -> val) { // 找到插入节点前一个位置,方便插入
prev = prev -> next;
}
// 进行调整
last_sort_node->next = cur -> next; // 移除调整节点
cur ->next = prev->next; //单链表插入首先将插入节点next修改(被插入位置修改后,找不到后继节点)
prev->next = cur;
}
cur = last_sort_node -> next; //这里考虑修改后节点cur指向,所以变成了 last_sort_node -> next
}
cur = dummy -> next;
delete dummy;
return cur;
}
};
b. 归并排序
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == nullptr) {
return head;
}
int len = 0;
ListNode* node = head;
// 计算链表长度
while (node) {
++len;
node = node -> next;
}
// 使用一个辅助头节点
ListNode* dummy = new ListNode(0, head);
// 每次左移一位,表示该处链表归并排序完成
for (int sublen = 1; sublen < len; sublen <<= 1) {
ListNode* prev = dummy, * cur = dummy->next; //有可能第一个节点换掉了,所以这里不能写* cur = head
while (cur) {
ListNode* head1 = cur; //第一段节点的第一个节点, 下面找第二段节点的第一个节点
for (int i = 1; i < sublen && cur->next; ++i) { //防止有一部分节点长度不为sublen
cur = cur->next;
}
ListNode* head2 = cur->next; //当前 cur 为第二段节点的第一个节点前一个节点
cur->next = nullptr; //第一段与第二段断开
cur = head2; //将第二段与后面部分断开,注意考虑第二段cur为空情况
for (int i = 1; i < sublen && cur && cur->next; ++i) {
cur = cur->next;
}
ListNode* next_part = nullptr; //后面部分的头结点,为了防止cur为空,所以相比前一段分隔需要加上一个判断
if (cur) {
next_part = cur->next;
cur->next = nullptr;
}
ListNode* merged = merge(head1, head2); // 合并两端,返回一个头结点
prev->next = merged; //与上一个部分合并
while (prev->next) {
prev = prev->next;
} // prev指向合并后链表段最后一个元素
cur = next_part; //更新最后一个节点
}
}
node = dummy->next;
delete dummy;
return node;
}
ListNode* merge(ListNode* head1, ListNode* head2) { //参考题目:合并两个有序列表题
ListNode* dummy = new ListNode(0);
ListNode* l1 = head1, *l2 = head2, *cur = dummy;
while (l1 && l2) {
if (l1->val < l2->val) {
cur->next = l1;
l1 = l1->next;
}else {
cur->next = l2;
l2 = l2->next;
}
cur = cur->next;
}
while (l1) {
cur->next = l1;
l1 = l1->next;
cur = cur->next;
}
while (l2) {
cur->next = l2;
l2 = l2->next;
cur = cur->next;
}
cur = dummy->next;
delete dummy;
return cur;
}
};
c.堆排序
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
struct node {
int val;
ListNode* ptr;
bool operator> (const node& that) const { //构建小根堆,greater,重载大于号
return val > that.val;
}
};
public:
ListNode* sortList(ListNode* head) {
if (head == nullptr) {
return head;
}
priority_queue <node,vector<node>,greater<node>> pq; //注意修改排序方式,默认为大根堆;
while (head) {
pq.push({head->val, head});
head = head->next;
}
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
while (!pq.empty()) {
auto p = pq.top();
pq.pop();
p.ptr->next = nullptr;
cur->next = p.ptr;
cur = cur->next;
}
cur = dummy->next;
delete dummy;
return cur;
}
};
方法a:时间复杂度:O( n 2 n^2 n2)。
空间复杂度:O(n)。
方法b:时间复杂度:O(nlogn)。
空间复杂度O(1)。
方法c:时间复杂度:O(nlogn)。
空间复杂度O(n)。
其他补充:
对链表进行插入排序
两个链表的第一个公共节点
思路
a.直接利用哈希表,如果命中一个。我们需要返回命中的那一个。(略)
b.双指针,同时从headA,headB出发,如果对于其中一个指针第一次为空指向另个一节点头继续遍历;当且仅当出现相同节点时候,如果非空:有相交节点(当前指向);否则没有。
核心代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (headA == nullptr || headB == nullptr) { // 只要一个为空节点,保证不相交
return nullptr;
}
ListNode* pA = headA, *pB = headB;
while (pA != pB) { // 注意,最差情况:pA,pB以不同顺序一定会把两个链表访问一次
//不相交一定会有pa == pb ==nullptr
pA = pA == nullptr? headB : pA ->next;
pB = pB == nullptr? headA : pB->next;
}
return pA;
}
};
时间复杂度O(n+m)。
空间复杂度O(1)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode *p1 = list1, *p2 = list2;
ListNode *dummy = new ListNode(0); // 添加一个辅助的头结点,比较好操作
ListNode *p = dummy;
while (p1 && p2) { // 两个指针指向有效元素
if (p1 -> val < p2 -> val) {
p -> next = p1;
p1 = p1->next;
} else {
p -> next = p2;
p2 = p2 -> next;
}
p = p->next; //当前合并后链表节点指针后移
}
// 将剩下节点全部合并到新节点中
while (p1) {
p -> next = p1;
p1 = p1 -> next;
p = p -> next;
}
while (p2) {
p -> next = p2;
p2 = p2 -> next;
p = p -> next;
}
// 注意释放辅助节点
p = dummy->next;
delete dummy;
return p;
}
};
b. 递归/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(list1 == nullptr) {
return list2; //返回非空的头结点
} else if (list2 == nullptr) {
return list1;
} else if (list1 -> val < list2 -> val) { //将问题变为list1的后半部分和list2合并,返回头结点(这里将函数压入系统栈过程,实际在将问题规模简化)
list1->next = mergeTwoLists(list1->next, list2); //返回的头结点就是list1的下一个节点,做一定处理(注意每次处理是递归函数返回阶段)
return list1;
} else {
list2 -> next = mergeTwoLists(list2->next,list1);
return list2;
}
return nullptr;
}
};
a.迭代:时间复杂度O(n + m)。
空间复杂度O(1)。
b.递归:时间复杂度O(n + m)。
空间复杂度O(n + m)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
return merge(lists, 0, lists.size() - 1);
}
ListNode* merge_twolist(ListNode* headA, ListNode* headB) { //旧题目:合并两个有序链表,另一种简单写法
if ((!headA) || (!headB)) {
return headA ? headA : headB; //返回非空链表头结点
}
ListNode head, *cur = &head, *ptra = headA, *ptrb = headB;
while (ptra && ptrb) {
if (ptra -> val < ptrb ->val) {
cur->next = ptra;
ptra = ptra->next;
} else {
cur->next = ptrb;
ptrb = ptrb->next;
}
cur = cur->next;
}
cur->next = (ptra? ptra : ptrb);
return head.next;
}
ListNode* merge(vector<ListNode*>& lists, int l, int r) {
if (l == r) {
return lists[l]; //分开
} else if (l > r) {
return nullptr; // 没有排序对象
}
int mid = l + ((r - l) >> 1);
return merge_twolist(merge(lists, l, mid), merge(lists, mid + 1, r));
}
};
a.分治法:时间复杂度O(knlogk)。
空间复杂度O(logk)
b.堆排序:时间复杂度O(knlogk)。
空间复杂度O(k)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
vector<ListNode*> splitListToParts(ListNode* head, int k) {
// 首先统计链表长度,然后根据长度进行分隔即可
int len = 0;
ListNode* cur = head;
while (cur) {
cur = cur->next;
++len;
}
vector<ListNode*> ans(k, nullptr);
//每段至少分为part_len个,余下的部分前面部分一个part一份
int part_len = len / k, reminder = len % k;
cur = head;
for (int i = 0; i < k && cur; ++i) { //注意 cur为空时,计算完长度后,与下一部分断开code会报错
ans[i] = cur;
int part_size = part_len + (i < reminder? 1 : 0); //具体每一个部分的长度
for (int j = 1; j < part_size; ++j) {
cur = cur->next;
}
ListNode* cur_next = cur->next;
cur->next = nullptr; //断开
cur = cur_next;
}
return ans;
}
};
时间复杂度O(n)。
空间复杂度O(1)
其他补充:
1.合并两个链表
2.分割链表
两数相加
思路
本题代码基本和合并两个链表类似。
注意进位和两个数字位数不等的时候。
核心代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
// 注意这里数据存储按照类似小端方式
ListNode dummy, *tail = &dummy;
bool carry = false; //表示是否进位
while (l1 || l2) { // l1与l2全部为空,结束循环
int n1 = l1 ? l1 -> val : 0;
int n2 = l2 ? l2 -> val : 0;
int sum = n1 + n2 + carry;
//更新节点数据和进位数据
ListNode* next_cur = new ListNode(sum % 10);
tail->next = next_cur;
tail = tail->next;
carry = sum / 10 == 1 ? true : false;
// 更新l1,l2
if (l1) {
l1 = l1 -> next;
}
if (l2) {
l2 = l2 -> next;
}
}
// 注意可能进位操作,多一位
if(carry) {
tail->next = new ListNode(1);
}
return dummy.next;
}
};
时间复杂度O(n)。
空间复杂度O(1)。
其他补充:
链表求和
两数相加 II
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if (k == 0 || head == nullptr || head->next == nullptr) { //排除不需要继续算的数据
return head;
}
// 由于要连接成环,我们需要定位在最后一个节点上。
int len = 1;
ListNode* cur = head;
while (cur -> next) {
++len;
cur = cur->next;
}
// 如果旋转长度为len的倍数,直接返回
k = len - k % len;
if (k == len) {
return head;
}
//封闭成环
cur -> next = head;
while (k--) {
cur = cur->next;
}
// 断开并且记录新的头结点
head = cur->next;
cur->next = nullptr;
return head;
}
};```
>时间复杂度O(n)。
空间复杂度O(1)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
ListNode* head;
public:
Solution(ListNode* head) {
this->head = head;
}
int getRandom() {
int ans = 0, pos = 1;
for (auto node = head; node; pos++, node = node -> next) {
if (rand() % pos == 0) {
ans = node -> val; //改变数据
}
}
return ans;
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(head);
* int param_1 = obj->getRandom();
*/
蓄水池抽样方法:时间复杂度初始化O(1),随机采样O(n)。
空间复杂度均为O(1)。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return head;
}
unordered_map<Node*, Node*> mp;
// 创建映射关系[旧节点,新节点]
for(auto node = head; node; node = node -> next) {
Node* new_ptr = new Node(node->val);
mp[node] = new_ptr;
}
//依次串好
for (auto[ori_node , new_node] : mp) {
new_node->next = mp.count(ori_node->next) ? mp[ori_node->next] : nullptr; //注意指向空节点,mp内没有
new_node->random = mp.count(ori_node->random) ? mp[ori_node->random] : nullptr;
}
return mp[head];
}
};
b 直接迭代:/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return nullptr;
}
//首先原地拷贝
for (auto node = head; node; node = node->next->next) {
Node* new_node = new Node(node->val);
new_node -> next = node -> next;
node->next = new_node;
}
//然后将new_node的随机指针设置好
for (auto node = head; node; node = node->next->next) {
Node* new_node = node->next;
new_node->random = node->random ? node->random->next : nullptr;
}
Node* new_head = head->next; // 记录新的头结点
// 最后将两个部分分离
for (auto node = head; node; node = node->next) { //分离时候由于指针改变,只要移动一次
Node* new_node = node->next;
node->next = new_node->next;
new_node->next = new_node->next ? new_node->next->next : nullptr;
}
return new_head;
}
};
a,b方法时间复杂度O(n)。
a方法空间复杂度O(n),b方法空间复杂度O(1)。