《程序员代码面试指南》it名企算法与数据结构题目最优解(第二版)刷题笔记6

本文介绍了数据结构中栈和队列的应用,通过具体实例讲解了如何优化子数组问题的算法,从O(N^3)降低至O(N),并探讨了链表的常见问题及其解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于之前看了牛客网的数据结构和算法的课程知道了左神,现在找到了这本书当作入门书做做吧,虽然书的题解都是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;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值