以下题目来源于leetcode
有两个链表,它们表示逆序的两个非负数。计算出两个数的和之后,同样逆序输出作为一个链表
比如:(2->1->3)+ (5->6->4)
得到:7->0->8
//Add two Numbers
class Solution
{
public:
ListNode *AddTwoNumbers(ListNode* l1, ListNode* l2)
{
ListNode dummy(-1);//投节点
int carry = 0;
ListNode* prev = &dummy;
for (ListNode* pa = l1, *pb = l2; pa != NULL || pb != NULL;
pa = pa == nullptr ? 0 : pa->next, pb = pb == nullptr ? 0 : pb->next,
prev = prev->next)
{
const int a = pa == nullptr ? 0 : pa->val;
const int b = pb == nullptr ? 0 : pb->val;
const int value = (a + b + carry) % 10;
carry = (a + b + carry) / 10;
prev->next = new ListNode(value);//尾插
}
if (carry > 0)
prev->next = new ListNode(carry);
return dummy.next;
}
};
把链表中m到n的部分反转(1<=m<=n<=length)。
注意要求:在原地反转,也就是不能申请额外的空间,且只能遍历一遍。
比如:1->2->3->4->5->nullptr,m=2,n=4
return 1->4->3->2->5->nullptr
1<=m<=n<=length
ListNode* reverseBetween(ListNode *head, int m, int n)
{
ListNode dummy(-1);
dummy.next = head;
ListNode *prev = &dummy;
for (int i = 0; i < m - 1; i++)
{
prev = prev->next;
}
ListNode* const head2 = prev;
prev = head2->next;
ListNode* cur = prev->next;
for (int i = m; i < n; i++)
{
prev->next = cur->next;
cur->next = head2->next;
head2->next = cur;
cur = prev->next;
}
return dummy.next;
}
给定一个单链表和一个x,把链表中小于x的放到前面,大于等于x的放到后面,每部分元素的原始相对位置不变。
遍历一遍链表,把小于x的都挂到head1后,把大于等于x的都放到head2后,最后再把大于等于的链表挂到小于链表的后面就可以了。
ListNode* partition(ListNode* head, int x)
{
ListNode left_dummy(-1);//头结点
ListNode right_dummy(-1);//头结点
auto left_cur = &left_dummy;
auto right_cur = &right_dummy;
for (ListNode* cur = head; cur; cur = cur->next)
{
if (cur->val < x)
{
left_cur->next = cur;
left_cur = cur;
}
else
{
right_cur->next = cur;
right_cur = cur;
}
}
left_cur->next = right_dummy.next;
right_cur->next = nullptr;
return left_dummy.next;
}
去除链表中的重复元素,使之只出现一次。
比如:1->1->2,return 1->2
1->1->2->3->3,return 1->2->3
ListNode *delDuplicates(ListNode* head)
{
if (head == nullptr)
return nullptr;
for (ListNode* prev = head, *cur = head->next; cur; cur = cur->next)
{
if (prev->val == cur->val)
{
prev->next = cur->next;
delete cur;
}
else
prev = cur;
}
return head;
}
去除链表中的重复元素,使之一次也不出现。
比如:1->2->3->3->4->4->5 return 1->2->5
1->1->1->2->3 return 2->3
//使链表中重复的元素一次也不出现
ListNode* delDuplicate(ListNode* head)
{
if (!head || !head->next)
return head;
ListNode* p = head->next;
if (head->val == p->val)
{
while (p && head->val == p->val)
{
ListNode* tmp = p;
p = p->next;
delete p;
}
delete head;
return delDuplicate(p);
}
else
{
head->next = delDuplicate(head->next);
return head;
}
}
在k位置处旋转链表
比如:->2->3->4->5 k = 2
return 4->5->1->2->3
先遍历一遍,得出链表长度len.注意k可能大于len,所以令 k%=len,,将尾节点next指针指向首节点,形成一个环,接着往后走len-k步,从这里断开环,就是要求的结果了。
ListNode* rotateRight(ListNode* head, int k)
{
if (head == nullptr || k == 0)
return head;
int len = 1;
ListNode* p = head;
while (p->next)
{
len++;
p = p->next;
}
k = len - k%len;
p->next = head; // 首尾相连构成环
for (int step = 0; step < k; step++)
{
p = p->next;//接着向后走
}
head = p->next; //新的首节点
p->next = nullptr;//断开环
return head;
}
成对交换节点
比如:1->2->3->4 return 2->1->4->3
题目规定不能直接交换两个节点的值,所以我们只能交换两个节点。
ListNode* swapPairs(ListNode* head)
{
if (head == NULL)
return NULL;
if (head->next == NULL)
return head;
ListNode* tmp = head->next;
head->next = swapPairs(tmp->next);
tmp->next = head;
return tmp;
}
给定一个链表,把最后一个结点插入到第1个结点后,倒数第二个结点插入到第2个结点后,倒数第三个结点插入到第3个结点后,以此类推……
找到中间节点,将后面的链表reverse,再合并前后的两部分
//找到中间位置,翻转后面的链表并合并
void RecorderList(ListNode *head)
{
if (head == NULL || head->next == NULL)
return;
ListNode* slow = head, *fast = head, *prev = NULL;
while (fast && fast->next)
{
prev = slow;
slow = slow->next;
fast = fast->next->next;
}
prev->next = NULL;//cut at middle
slow = Reverse(slow);
//merge two lists
ListNode *cur = head;
while (cur->next)
{
ListNode* tmp = cur->next;
cur->next = slow;
slow = slow->next;
cur->next->next = tmp;
cur = tmp;
}
cur->next = slow;
}
ListNode* Reverse(ListNode* head)
{
ListNode dummy(-1);//头节点
dummy.next = head;
ListNode* prev = head;
ListNode* cur = prev->next;
while (cur)
{
prev->next = cur->next;
cur->next = dummy.next;
dummy.next = cur;
cur = prev->next;
}
return dummy.next;
}
把原始链表k个k个的反转,如果最后剩余的不到k个结点,那么保持不变。
这道题让我们以每k个为一组来翻转链表,实际上是把原链表分成若干小段,然后分别对其进行翻转,那么肯定总共需要两个函数,一个是用来分段的,一个是用来翻转的,我们就以题目中给的例子来看,对于给定链表1->2->3->4->5,一般在处理链表问题时,我们大多时候都会在开头再加一个dummy node,因为翻转链表时头结点可能会变化,为了记录当前最新的头结点的位置而引入的dummy node,那么我们加入dummy node后的链表变为-1->1->2->3->4->5,如果k为3的话,我们的目标是将1,2,3翻转一下,那么我们需要一些指针,pre和next分别指向要翻转的链表的前后的位置,然后翻转后pre的位置更新到如下新的位置:
-1->1->2->3->4->5
| |
pre next
-1->3->2->1->4->5
| |
pre next
//将链表元素k个k个的旋转
ListNode* reversekGroup(ListNode* head, int k)
{
if (!head || k == 1)
{
return head;
}
ListNode dummy(-1);
ListNode* prev = &dummy, *cur = head;
dummy.next = head;
int i = 0;
while (cur)
{
++i;
if (i % k == 0)
{
prev = reverseOnegroup(prev, cur->next);
cur = prev->next;
}
else
{
cur = cur->next;
}
}
return dummy.next;
}
ListNode* reverseOnegroup(ListNode* prev, ListNode* next)
{
ListNode *last = prev->next;
ListNode *cur = last->next;
while (cur != next)
{
last->next = cur->next;
cur->next = prev->next;
prev->next = cur;
cur = last->next;
}
return last;
}
设计并实现一个支持get和set操作的缓存:
get(key) - 存在时返回其值,否则返回-1;
set(key) - 不存在时插入新值,存在时更新其值,注意当容量满时,需删除最长时间没有访问的key,将其删除,并插入新的key。
这题的大致思路就是用一个hash表来保存已经存在的key, 然后用另外一个线性容器来存储其key-value值, 我们可以选择链表list, 因为需要调整结点的位置, 而链表可以在O(1)时间移动结点的位置, 数组则需要O(n).
如果新来一个set请求, 我们先去查hash表
1. 如果已经存在了这个key, 那么我们需要更新其value, 然后将其在list的结点位置移动到链表首部.
2. 如果不存在这个key, 那么我们需要在hash表和链表中都添加这个值, 并且如果添加之后链表长度超过最大长度, 我们需要将链表尾部的节点删除, 并且删除其在hash表中的记录
如果来了一个get请求, 我们仍然先去查hash表, 如果key存在hash表中, 那么需要将这个结点在链表的中的位置移动到链表首部.否则返回-1.
class LRUcache
{
public:
LRUcache(int capacity)
{
_capacity = capacity;
}
int get(int key)
{
if (cacheMap.find(key) == cacheMap.end())
return -1;
//把当前访问节点移到链表头部,并且更新map中该节点的位置
cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
cacheMap[key] = cacheList.begin();
return cacheMap[key]->value;
}
void set(int key, int value)
{
if (cacheMap.find(key) == cacheMap.end)
{
//删除链表尾部节点(最少访问的节点)
if (cacheList.size() == _capacity)
{
cacheMap.erase(cacheList.back().key);
cacheList.pop_back();
}
//插入新节点到链表头部,并且在map中增加该节点
cacheList.push_front(CacheNode(key, value));
cacheMap[key] = cacheList.begin();
}
else
{
//更新节点的值,把当前访问的节点移到链表头部,并且更新map中该节点的地址
cacheMap[key]->value = value;
cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
cacheMap[key] = cacheList.begin();
}
}
private:
struct CacheNode
{
int key;
int value;
CacheNode(int k, int v)
:key(k)
, value(v)
{}
};
list cacheList;
unordered_map<int, list ::iterator> cacheMap;
int _capacity;
};