707.设计链表
时间复杂度: 涉及 index 的相关操作为 O(index), 其余为 O(1)
空间复杂度: O(n)
#其他语言版
#include <iostream>
using namespace std;
int main() {
int index = 2;
while(--index){ // index-- 输出 1 0 --index输出 1
cout << index << " ";
}
}
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
这道题目设计链表的五个接口:
获取链表第index个节点的数值
在链表的最前面插入一个节点
在链表的最后面插入一个节点
在链表第index个节点前面插入一个节点
删除链表的第index个节点
#include <iostream>
#include <vector>
using namespace std;
class MyLinkedList{
public:
struct LinkedNode{
int val;
LinkedNode* next;
LinkedNode(int x):val(x),next(NULL){}
};
MyLinkedList(){
_dummyHead = new LinkedNode(0);
_size = 0;
}
int get(int index){
if(index > _size - 1 || index < 0){
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val){
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
_dummyHead->next = newNode;
_size++;
}
void addAtTail(int val){
LinkedNode* newNode =new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != NULL){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
void addAtIndex(int index, int val){
if(index > _size) return;
if(index < 0) index = 0;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--){
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
void deleteAtIndex(int index){
if (index >= _size || index < 0){
return;
}
LinkedNode* cur = _dummyHead;
while(index--){
cur = cur->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = NULL;
_size--;
}
void printLinkedList(){
LinkedNode* cur = _dummyHead;
while(cur->next != NULL){
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode* _dummyHead;
};
int main(){
MyLinkedList* obj = new MyLinkedList();
obj->addAtHead(1);
obj->printLinkedList();
obj->addAtIndex(0,2);
obj->printLinkedList();
obj->deleteAtIndex(0);
obj->printLinkedList();
return 0;
}
206.反转链表
- 时间复杂度: O(n)
- 空间复杂度: O(1)
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
//双指针法
#include <iostream>
#include <vector>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode(int x);val(x),next(NULL)
};
class Solution{
public:
ListNode* reverseList(ListNode* head){
ListNode* cur = head;
ListNode* pre = NULL;
ListNode* tmp;
while(cur){
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
int main(){
vector<int> nums = {1, 2, 3, 4, 5};
LinkNode* head = new ListNode(nums[0]);
LinkNode* cur = head;
for(int i = 1; i <nums.size(); i++){
cur->next = new ListNode(nums[i]);
cur = cur->next;
}
Solution s;
ListNode* pre = s.reveserList(head);
while(pre){
cout << pre->val << " ";
pre = pre->next;
}
}
//递归法
#include <iostream>
#include <vector>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode(int x) : val(x),next(NULL){}
};
class Solution{
public:
ListNode* reverseList(ListNode* head) {
// 边缘条件判断
if(head == NULL) return NULL;
if(head->next == NULL) return head; //最后节点指向自己
// 递归调用,翻转第二个节点开始往后的链表
ListNode* last = reverseList(head->next); // 倒数第二个节点
// 翻转头节点与第二个节点的指向
head->next->next = head;
// 此时的head节点为尾节点,next需要指向 NULL
head->next = NULL;
return last;
}
};
int main(){
Solution s;
//ListNode* head = new ListNode(1);
//ListNode* node2 = new ListNode(2);
vector<int> v = {1, 2, 3, 4};
ListNode* head = new ListNode(v[0]);
ListNode* p = head;
for(int i = 1; i < v.size(); ++i){
p->next = new ListNode(v[i]);
p = p->next;
}
ListNode* res = s.reverseList(head);
while(res){
cout << res->val << " ";
res = res->next;
}
}
#容易理解的解释,网上Leetcode上面找到的 加了一点点改动,主要用于理解
/**
* 以链表1->2->3->4->5举例
* @param head
* @return
*/
#include <iostream>
#include <vector>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode(int x) : val(x),next(NULL){}
};
class Solution{
public:
ListNode* reverseList(ListNode* head) {
if (head == NULL || head->next == NULL) {
/*
直到当前节点的下一个节点为空时返回当前节点
由于5没有下一个节点了,所以此处返回节点5
*/
return head;
}
//递归传入下一个节点,目的是为了到达最后一个节点
ListNode* newHead = reverseList(head->next);
/*
第一轮出栈,head为5,head.next为空,返回5
第二轮出栈,head为4,head.next为5,执行head.next.next=head也就是5.next=4,
把当前节点的子节点的子节点指向当前节点
此时链表为1->2->3->4<->5,由于4与5互相指向,所以此处要断开4.next=null
此时链表为1->2->3->4<-5
返回节点5
第三轮出栈,head为3,head.next为4,执行head.next.next=head也就是4.next=3,
此时链表为1->2->3<->4<-5,由于3与4互相指向,所以此处要断开3.next=null
此时链表为1->2->3<-4<-5
返回节点5
第四轮出栈,head为2,head.next为3,执行head.next.next=head也就是3.next=2,
此时链表为1->2<->3<-4<-5,由于2与3互相指向,所以此处要断开2.next=null
此时链表为1->2<-3<-4<-5
返回节点5
第五轮出栈,head为1,head.next为2,执行head.next.next=head也就是2.next=1,
此时链表为1<->2<-3<-4<-5,由于1与2互相指向,所以此处要断开1.next=null
此时链表为1<-2<-3<-4<-5
返回节点5
出栈完成,最终头节点5->4->3->2->1
*/
head->next->next = head;
head->next = NULL;
return newHead;
}
};
int main(){
vector<int> v = {1,2,3,4,5};
ListNode* head = new ListNode(v[0]);
ListNode* p = head;
for (int i = 1;i < v.size();++i){
p->next = new ListNode(v[i]);
p = p->next;
}
Solution s;
ListNode* newHead = s.reverseList(head);
while (newHead){
cout << newHead->val << "->";
newHead = newHead->next;
}
}