反转链表
剑指 Offer 24. 反转链表
难度简单54
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL输出: 5->4->3->2->1->NULL
限制:
0 <= 节点个数 <= 5000
思路一:
虽然是链表的练习,但是算法的思路非常多。依旧我之前所说会用到各种办法,不是链表部分只用链表
本题:需要返回一个链表指针,这是一个新的链表。这个新的链表本质上是从旧的链表尾一个一个拿元素。也就是对于旧的链表而言,实际上是一个先进后出的一个情形。所以我们首先想到符合这个特点的就是栈。
从头开始压栈,压完之后一个一个出栈。放入新的链表中;
/** * 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 == NULL) return NULL; if(head->next == NULL) return head; stack<ListNode*> stack_listnode; while(head != NULL) { stack_listnode.push(head); head = head->next; } ListNode* q = stack_listnode.top(); ListNode* qHead = q; stack_listnode.pop(); while(stack_listnode.size() != 0) { q->next = stack_listnode.top(); stack_listnode.pop(); q = q->next; } q->next = NULL; return qHead; }};
思路二:
如果你是一个认认真真学习过数据结构的同学,你一定会想到的一个思路:头插法和尾插法!
说到这里想必你已经明白了。头插和尾插会在另一个md里面详细说明;
思路三:
简单的来说,通过两个指针来在给定链表上爬,在爬的过程中给第三个指针赋值,慢慢将第三个指针构成新的链表。有点像DNA的转录过程….
class Solution {public: ListNode* reverseList(ListNode* head) { ListNode *p1,*p2,*p3; p1 = NULL; p2 = head; while(p2!=NULL){ p3 = p2->next; p2->next = p1; p1 = p2; p2 = p3; } return p1; }};
删除中间结点
面试题 02.03. 删除中间节点
难度简单31
实现一种算法,删除单向链表中间的某个节点(即不是第一个或最后一个节点),假定你只能访问该节点。
示例:
输入:单向链表a->b->c->d->e->f中的节点c结果:不返回任何数据,但该链表变为a->b->d->e->f
思路:
本题的关键是在于理解题意。
题意分析:
题目只给你某一个结点,其他什么都不给,链表你也拿不到。让你删除这个元素,个人认为这是一个很好的案例。我拿不到链表,只能拿到结点。也不知道这是第几个…
但是我们可以访问这个结点之后的结点,也可以访问之后的之后的结点。所以把本结点等于next的值,然后把next删掉,偷梁换柱。
怎么删除呢?其实就是把node->next = node->next->next 直接赋值就行。
/** * 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) { if(node == NULL) return ; node->val = node->next->val; node->next = node->next->next; }};
回文链表
234. 回文链表
难度简单533
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2输出: false
示例 2:
输入: 1->2->2->1输出: true
进阶:
你能否用 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: bool isPalindrome(ListNode* head) { if(head == NULL) return true; if(head->next == NULL) return true; vector<int> array; int count = 0; while(head != NULL){ array.push_back(head->val); head = head->next; } for(int i = 0;i < array.size() / 2; i++){ if(array[i] != array[array.size() - i - 1]) return false; } return true; }};
思路二:
使用快慢指针,找到中间,把后半部分reverse,从中间开始比较。
class Solution {public: bool isPalindrome(ListNode* head) {//O(n)、O(1) ListNode* slow = head, *fast = head, *prev = nullptr; while (fast){//find mid node slow = slow->next; fast = fast->next ? fast->next->next: fast->next; } while (slow){//reverse ListNode* temp = slow->next; slow->next = prev; prev = slow; slow = temp; } while (head && prev){//check if (head->val != prev->val){ return false; } head = head->next; prev = prev->next; } return true; }};
链表求和
面试题 02.05. 链表求和
难度中等23
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例:
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295输出:2 -> 1 -> 9,即912
进阶:假设这些数位是正向存放的,请再做一遍。
示例:
输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295输出:9 -> 1 -> 2,即912
思路:
本题的思路很明确:链表的合并+进位问题。(链表的合并,会在链表专题中专门讲)
class Solution {public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode *sum = l1; while(l1->next && l2->next) { l1->val += l2->val; l1 = l1->next; l2 = l2->next; } l1->val += l2->val; if(!l1->next) { l1->next = l2->next; } l1 = sum; while(l1->next) { if(l1->val >= 10) { l1->val -= 10; l1 = l1->next; l1->val++; } else { l1 = l1->next; } } if(l1->val >= 10) { l1->val -= 10; l1->next = new ListNode(1); return sum; } else { return sum; } } };