【算法】【C++】利用树型关联式容器的经典题目


1. 复制带随机指针的链表

👉🔗题目链接

描述: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。
新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点。

思路:map 解决复杂链表的复制

🐎C语言写法:


/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */
struct Node* copyRandomList(struct Node* head) {
	if (head == NULL)
		return NULL;

	struct Node* cur = head;
	struct Node* copyhead = head;

	// 1. 
	// 遍历源节点,并将新开辟的复制结点,放在源节点之后
	while (cur)
	{
		struct Node* copy = (struct Node*)malloc(sizeof(struct Node));   // 新建结点
		copy->val = cur->val;   // 值
		copy->next = cur->next; // 链接
		cur->next = copy;  //链接

		// copy链表的头结点设置
		if (copyhead == head)
		{
			copyhead = copy;
		}

		cur = copy->next;
	}

	// 2.
	// 设置复制节点的random,如果该复制节点的源节点的random不为空,指向random,否则赋空
	cur = head;
	while (cur)
	{
		struct Node* copynode = cur->next;
		if (cur->random)
		{
			copynode->random = cur->random->next;
		}
		else
		{
			copynode->random = NULL;
		}

		cur = copynode->next;
	}

	// 3.
	// 把拷贝结点解下来,链接组成拷贝链表
	cur = head;
	while (cur)
	{
		struct Node* copynode = cur->next;
		cur->next = copynode->next;
		cur = cur->next;
		if (cur)
		{
			copynode->next = cur->next;
			copynode = copynode->next;
		}
		else
		{
			copynode->next = NULL;
		}
	}

	return copyhead;
}
🐎C++写法
1.先尾插复制出一个单链表
在这里插入图片描述
2.用 map 处理 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*> copyNodeMap;
		Node* cur = head;
		Node* copyhead = nullptr;
		Node* copytail = nullptr;
		// 1. 链接
		while (cur)
		{
			Node* copynode = new Node(cur->val);
			copyNodeMap[cur] = copynode;     // 插入+修改 copyNodeMap[源节点] = 复制节点(此时只有val 指针为null的)
			if (copytail == nullptr)
			{
				copytail = copyhead = copynode;
			}
			else
			{
				copytail->next = copynode;
				copytail = copytail->next;
			}
			cur = cur->next;
		}

		// 位置重置
		cur = head;
		Node* copynode = copyhead;

		// 2. 处理random
		while (cur)
		{
			if (cur->random == nullptr)
			{
				copynode->random = nullptr;
			}
			else
			{
				copynode->random = copyNodeMap[cur->random];	// 寻找
			}

			cur = cur->next;
			copynode = copynode->next;
		}
		return copyhead;
	}
};

2. 前K个高频单词

👉🔗题目链接

描述: 给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。
如果不同的单词有相同出现频率, 按 字典顺序 排序。

要求分析:
a. 统计次数 ----> map 统计频次,按 key 排(字典顺序)
b. 频次前k ----> 排序的 稳定性 考查
c. 相同频次按 字典顺序

思路: 排序稳定性、仿函数控制比较逻辑

🐎:方法一:stable_sort

// 方法一:stable_sort
class Solution {
public:
	struct Compare
	{
		bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2)
		{
			return kv1.second > kv2.second;
		}
	};
	vector<string> topKFrequent(vector<string>& words, int k) {
		// 1. 统计次数
		map<string, int> countMap;  // map排序是按key排的,不是频次value,此时已经是字典顺序
		for (auto& str : words)
		{
			countMap[str]++;
		}

		// 2. 按次序排序:同时,次序相同,要求字典顺序排序
		// 考查:排序的稳定性
		// sort 是快排,要求是随机迭代器。故不能作用到countmap的迭代器(双向迭代器)上。另:快排是不稳定的
		// 还是写一下sort的过程:将pair插入vecter再进行sort
		vector<pair<string, int>> v(countMap.begin(), countMap.end());
		//sort(v.begin(),v.end(), Compare());    // 这里是按first比,我们需要的是按second比,加一个仿函数
		// 实际上:C++提供了一个稳定的排序--stable_sort
		stable_sort(v.begin(), v.end(), Compare());

		vector<string> ret;
		for (size_t i = 0; i < k; ++i)
		{
			ret.push_back(v[i].first);
		}

		return ret;
	}
};

🐎: 方法二:sort,仿函数控制比较逻辑

// 方法二:sort,仿函数控制比较逻辑
class Solution {
public:
	struct Compare
	{
		bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2)
		{
			return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv2.first);
		}
	};
	vector<string> topKFrequent(vector<string>& words, int k) {
		// 1. 统计次数
		map<string, int> countMap;  // map排序是按key排的,不是频次value,此时已经是字典顺序
		for (auto& str : words)
		{
			countMap[str]++;
		}

		// 2. 按次序排序:同时,次序相同,要求字典顺序排序
		// 考查:排序的稳定性
		// sort 是快排,要求是随机迭代器。故不能作用到countmap的迭代器(双向迭代器)上。另:快排是不稳定的
		// sort的过程:将pair插入vecter再进行sort,并通过仿函数来控制“稳定性”!!!!
		// 注意:表面上看起来似乎是控制了稳定性,但实际上是控制的比较逻辑
		vector<pair<string, int>> v(countMap.begin(), countMap.end());
		sort(v.begin(), v.end(), Compare());    // 这里是按first比,我们需要的是按second比,加一个仿函数(这里是函数参数,传的是对象,这里加括号是传的匿名对象

		// 实际上:C++提供了一个稳定的排序--stable_sort
		//stable_sort(v.begin(),v.end(), Compare());

		vector<string> ret;
		for (size_t i = 0; i < k; ++i)
		{
			ret.push_back(v[i].first);
		}

		return ret;
	}
};

🐎 方法三:multimap 排序,模板参数中添加比较方法

// 方法三:multimap排序,模板参数中添加比较方法
// but!!!这题纯属运气好,multimap相等恰好是插入在右边的!!!如果是相等插入在左边就不通过喽
class Solution {
public:
	vector<string> topKFrequent(vector<string>& words, int k) {
		// 1. 统计次数
		map<string, int> countMap;  // map排序是按key排的,不是频次value,此时已经是字典顺序
		for (auto& str : words)
		{
			countMap[str]++;
		}

		// 2. 按次序排序:同时,次序相同,要求字典顺序排序
		// 考查:排序的稳定性
		// map 是排序+去重,我们要选用 multimap,同样可以传Compare(),我们对其进行指定比较逻辑
		multimap<int, string, greater<int>> sortMap;  // (类模板参数,传的是类型!!!
		for (auto& kv : countMap)
		{
			sortMap.insert(make_pair(kv.second, kv.first));
		}

		// 3. 取出前k个
		vector<string> ret;
		auto it = sortMap.begin();
		while (k--)
		{
			ret.push_back(it->second);
			it++;
		}
		return ret;
	}
};

🐎 方法四:multiset 排序,仿函数控制比较逻辑

// 方法四:multiset 排序,仿函数控制比较逻辑
// 实际上,指定比较逻辑后,可以使用 set 了,此时已经不是光比较 key 了
class Solution {
public:
	struct Compare
	{
		bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2) const // 有可能用const对象进行调用,所以加const
		{
			return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv2.first);
		}
	};
	vector<string> topKFrequent(vector<string>& words, int k) {
		// 1. 统计次数
		map<string, int> countMap;  // map排序是按key排的,不是频次value,此时已经是字典顺序
		for (auto& str : words)
		{
			countMap[str]++;
		}

		// 2. 按次序排序:同时,次序相同,要求字典顺序排序
		// 考查:排序的稳定性
		// multiset
		multiset<pair<string, int>, Compare> sortSet(countMap.begin(), countMap.end());

		// 3. 取出前k个
		vector<string> ret;
		auto it = sortSet.begin();
		while (k--)
		{
			ret.push_back(it->first);
			it++;
		}
		return ret;
	}
};

3. 两个数组的交集

👉🔗题目链接

描述: 给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

分析思路:

  1. set 排序 + 去重
  2. 找交集 a.不相等,小的++ b.相等,是交集同时++
  3. 一个集合走完就结束

找差集思路:

  1. 排序 + 去重
  2. 找差集 a.相等同时++ b.不相等,小的就是差集,小的++
  3. 一个走完了,剩下没有走完的也是差集
class Solution {
public:
	vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
		// 1. 排序 + 去重
		// sort(nums1.begin(), nums1.end());
		// unique(nums1.begin(), nums1.end());
		set<int> s1(nums1.begin(), nums1.end());
		set<int> s2(nums2.begin(), nums2.end());

		// 2. 找交集
		auto it1 = s1.begin();
		auto it2 = s2.begin();
		vector<int> ret;
		while (it1 != s1.end() && it2 != s2.end())
		{
			// a. 如果相等,是交集
			if (*it1 == *it2)
			{
				ret.push_back(*it1);
				++it1;
				++it2;
			}
			// b. 如果不等,小的那个++
			else
			{
				*it1 < *it2 ? ++it1 : ++it2;
			}
		}
		return ret;
	}
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值