剑指offer
第一天
剑指 Offer 09. 用两个栈实现队列
->点这里进入leetcode
这道题的意思就是让我们使用两个栈实现一个队列,
简单复习一下:
栈:先进后出(栈顶进出元素)
队列:先进先出(队列头出元素,队列尾进元素)
我们会发现栈可以做到队列尾进元素,但是如何让栈底出元素是一个问题
我们会想到将一个栈的元素依次出栈放到另外一个栈中的时候,他的元素是不是会倒过来,例如:
这样我们是不是就可以在A栈进元素,B栈出元素构成了一个先进先出的队列。
代码:
class CQueue {
public:
stack<int> a;
stack<int> b;
CQueue() {
}
//添加元素
void appendTail(int value) {
a.push(value); //元素进队列直接将元素放入a栈即可
}
//删除元素
int deleteHead() {
int e;
if(a.empty()) //这个很重要,如果a栈空会出错
return -1;
while(!a.empty()) //将a栈元素->b栈
{
e = a.top();
a.pop();
b.push(e);
}
int res = b.top(); //提取并删除b栈栈顶元素
b.pop();
while(!b.empty()) //将b栈元素->a栈
{
e = b.top();
b.pop();
a.push(e);
}
return res;
}
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
剑指 Offer 30. 包含min函数的栈
->点击这里进入leetcode
本题难点:
我们第一时间想到的肯定是将整个栈遍历一遍,找最小的元素,但是它会调用多次,每一次的时间复杂度都是O(n),这样肯定会超时。
解题思路:
建立两个栈:
数据栈A:存放栈的数据,入栈、出栈、获取栈顶元素都是正常的逻辑
辅助栈B:实现栈A中的最小元素始终对应着栈B的栈顶元素。
这样和普通的栈有什么不一样呢?
其中
pop() 时两栈栈顶元素同时出栈
push():时A栈正常,B栈则需要对比B栈栈顶元素和进栈元素,谁小,让谁进栈,这样就达成了栈A中的最小元素始终对应着栈B的栈顶元素(注意:当B栈为空的时候元素直接进栈即可)
**min()😗*取B栈栈顶元素即可。
举例:
代码:
class MinStack {
public:
stack<int> s;//栈A
stack<int> ad;//辅助栈
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
s.push(x);
if(ad.empty()) ad.push(x);//栈B为空,直接进栈
else
{
if(x < ad.top()) ad.push(x);
else ad.push(ad.top());
}
}
void pop() {
s.pop();
ad.pop();
}
int top() {
return s.top();
}
int min() {
return ad.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(x);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->min();
*/
当然,它还可以优化,可以将栈B重复元素去掉,减少空间复杂度,这样只用在pop()和min()的时候判断一下就可以了。
第二天
剑指 Offer 06. 从尾到头打印链表
这道题比较简单直接上代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int> res;
ListNode* p;
p = head;
while(p)
{
res.insert(res.begin(),p->val);
p = p->next;
}
return res;
}
};
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
这道题也比较简单:
/**
* 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) return head;
//ListNode* head2 = (ListNode*)malloc(sizeof(ListNode));
ListNode* head2 = new ListNode;
ListNode* p = head;
ListNode* q = p->next;
head2->next = NULL;
while(q)
{
p->next = head2->next;
head2->next = p;
p=q;
q = q->next;
}
head2->val = p->val;
return head2;
}
};
其实以上两道题我都使用了头插法的思想,你可以把输入的链表想象成一个序列,而用头插法就可以把他们反过来
剑指 Offer 35. 复杂链表的复制
本题难点:
加入了random指针,我们按照正常的思路复制的话,random指针难以处理;
解题方案:
使用哈希表映射,我们将每一个节点都复制一遍,也就是哈希表每个元素对应的映射,在复制random指针的时候,就可以在新建立的链表中根据映射找到对应的节点:
代码:
/*
// 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) {
map<Node*,Node*> m;
Node* p = head;
while(p)
{
m[p] = new Node(p->val);
p = p->next;
}
p = head;
while(p)
{
m[p]->next = m[p->next];
m[p]->random = m[p->random];
p = p->next;
}
return m[head];
}
};