由于之前看了牛客网的数据结构和算法的课程知道了左神,现在找到了这本书当作入门书做做吧,虽然书的题解都是java实现的,但好在用c++实现难度不大。
第一章 栈和队列
题目一:最大值减去最小值小于或等于num的子数组数量
给定数组arr和整数num,返回共有多少个子数组满足如下情况:
max(arr[i…j]) - min(arr[i…j]) <= num
普通的解法,找到arr的所有子数组,一共有O(N^2)个,然后对每一个数组遍历找到其中的最小值和最大值,这个过程的时间复杂度为O(N),然后看看这个子数组是否满足条件,统计数量即可,容易实现,但是时间复杂度为O(N^3)。最优解可以做到时间复杂度为O(N),空间复杂度为O(N):所有下标最多进max和min一次,出max和min一次。i和j的值不断增加,并且从来不减少,所以整个过程的时间复杂度为O(N)。
//! @file findTheNumberOfMaxreduceMinLOETnum.cpp
//! @author 鶸.
//! @date 2019/4/3
//! @brief
// 1、如果子数组arr[i…j]满足条件,那么arr[i…j]中的子数组一定也满足条件。
// 2、如果子数组arr[i…j]不满足条件,那么包含arr[i…j]的子数组一定也不满足条件。
// 3、用双端队列来维护max和min的更新,详细可以看我之前的"生成窗口最大值数组"问题
#include "stdafx.h"
#include <deque>
#include <iostream>
#include <vector>
using namespace std;
int getTheNumber(vector<int> arr,int num){
deque<int> max,min;int number=0,i=0,j=0;//由题意得,i和j在两个for循环之前定义便于计算number
for (;i<arr.size();++i)
{
for (;j<arr.size();++j)
{
while(!max.empty()&&arr[max.back()]<=arr[j])
max.pop_back();
max.push_back(j);
while(!min.empty()&&arr[min.back()]>=arr[j])
min.pop_back();
min.push_back(j);
if ((max.front()-min.front())>num)
{
break;
}
}
//如果j=i则不是数组了
number+=j-i;
//双端队列的好处
if (max.front()==i)
{
max.pop_front();
}
if (min.front()==i)
{
min.pop_front();
}
}
return number;
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> a;
a.push_back(3);
a.push_back(2);
a.push_back(5);
a.push_back(1);
a.push_back(4);
a.push_back(7);
a.push_back(8);
a.push_back(6);
cout<<getTheNumber(a,4);
return 0;
}
第二章 链表问题
题目一:打印两个有序链表的公共部分
给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。
//! @file printfThePublicPartOfTwoList.cpp
//! @author 鶸.
//! @date 2019/4/3
//! @brief 本题难度很低,因为是有序链表,所以直接从链表头开始比大小
#include <iostream>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
void printThePart(ListNode *head1,ListNode *head2){
while(head1!=NULL||head2!=NULL){
if (head1->val<head2->val)
{
head1=head1->next;
}
else if (head2->val<head1->val)
{
head2=head2->next;
}
else{
cout<<head1->val<<" ";
head1=head1->next;
head2=head2->next;
}
}
}
题目二:在单链表和双链表中删除倒数第k个节点
单链表
先明确一点,如果要删除链表的头节点之后的某个节点,实际上需要找到要删除节点的前一个节点。
先来看看单链表如何调整。如果链表为空或者K值小于1,这种情况下,参数是无效的,直接返回即可。除此之外,让链表从头开始走到尾,每移动一步,就让k的值减1。 如果K值小于0,如何找到要删除节点的前一个节点呢?方法如下:
1.重新从头节点开始走,每移动一步,就让K的值加1。
2.当K等于0是,移动停止,移动到的节点就是要删除节点的前一个节点。
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
ListNode* removeLastKthNode(ListNode *head, int n) {
ListNode* head1 = head;
if (head == NULL||n<1)return NULL;
int k = n;
while (head != NULL) {
head = head->next;
k--;
}
if (k > 0)return NULL;
if (k = 0)return head1->next;
head = head1;
while (k != 0) {
head = head->next;
k++;
}
head = head->next->next;
return head1;
}
双链表
几乎与单链表处理方式一样
struct DoubleListNode {
int val;
DoubleListNode *last;
DoubleListNode *next;
DoubleListNode(int x) : val(x), next(NULL) {}
};
DoubleListNode* removeLastKthNode(DoubleListNode *head, int n) {
DoubleListNode* head1 = head;
if (head == NULL||n<1)return NULL;
int k = n;
while (head != NULL) {
head = head->next;
k--;
}
if (k > 0)return NULL;
if (k = 0)return head1->next;
head = head1;
while (k != 0) {
head = head->next;
k++;
}
head = head->next->next;
if (head->next != NULL)
head->next->last = head;
return head1;
}