1.给定两个数组,编写一个函数来计算它们的交集(哈希表)(简单题)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 存放结果
unordered_set<int> nums_set(nums1.begin(), nums1.end());//把vector存入到set中
//vector中也一样,vector<int> nums(b.begin(),b.end())把b中的元素都赋值给a
for (int num : nums2)//增强型for循环,把nums2中的元素遍历赋值给num
{
// 发现nums2的元素 在nums_set里又出现过
if (nums_set.find(nums2) != nums_set.end()) {
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());//把set存入vector中
}
};
//注意vector中没有find函数,要使用的话得添加algorithm头文件,注意区别vector中push-back和insert的函数,
整个大体的流程为:两个数组存放在vector中,但vector没有find函数,把其中一个元素赋值给set,在set中查找
是否另一个数组的元素在第一个数组出现过,如果出现过,就插入到事先定义好的结果中,最后再返回结果
vector是尾部插入数字,insert有三种用法:
insert() 函数有以下三种用法:
1、在指定位置loc前插入值为val的元素,返回指向这个元素的迭代器it=nums.insert(begin()+loc-1,val)
2、在指定位置loc前插入num个值为val的元素 nums.insert(begin()-1+loc,num,val)
3、在指定位置loc前插入区间[start, end)的所有元素
//注意vector中没有find函数,需要加头文件algorithm,sort,copy,reserve也是一样
//注意insert返回的是指向的插入元素的迭代器
2.给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。(简单题)
解题思路:定义一个哈希表,用find查找另一个数是否在哈希表中,并返回指定元素的迭代器,没有查找到就把数组中的元素
一个个插入到哈希表中,如果找到了,就返回至两个元素的下表值。
解法1:
class solution{
vector<int> twoSum(vector<int> &nums, int target){
unordered_map<int,int> temp;
for(int i=0;i<nums.size();i++){
auto iter=temp.find(target-nums[i])
if(iter==temp.end()){//注意这里是两个等号
temp.insert(pair <int,int>(nums[i],i));//注意这里insert的插入方式
}
else{
return {iter->next,i};//这里是it迭代器访问,不是temp访问
}
}
return {};
}
}
//区分是iter访问还是元素访问,返回的是指向某一元素的迭代器,就是指针
//2.向哈希表中添加元素
1).insert 函数
m.insert(pair<int,int>(1, 10));
m.insert(pair<int,int>(nums[i],i))//注意这里insert的使用方式
m.insert(pair<int,int>(2, 20));
1
2).用数组方法直接添加
m[i]=nums[i];
m[nums[i]]=i;
m[4]=40;
解法2:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hashtable;
for (int i = 0; i < nums.size(); ++i) {
auto it = hashtable.find(target - nums[i]);
if (it != hashtable.end()) {
return {it->second, i};
}
hashtable[nums[i]] = i;
}
return {};
}
};
3.给你两个非空 的链表,表示两个非负的整数。它们每位数字都是按照?逆序?的方式存储的,并且每个节点只能存储?一位?数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0?开头。(中档题)
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *result = new ListNode(0);//ListNode list=new ListNode(0) 初始化一个节点值为0的空节点,最常用最正规写法
ListNode *tmp = result;
int sum = 0;
while(l1 || l2){
if(l1){
sum += l1 -> val;
l1 = l1 -> next;
}
if(l2){
sum += l2 -> val;
l2 = l2 -> next;
}
tmp -> next = new ListNode(sum % 10);//存在头结点,头结点为空,取余数操作,和放在第一个节点
sum /= 10;
tmp = tmp -> next;
}
if(sum){
tmp -> next = new ListNode(1);//temp的下一个节点值为1,进位值
}
return result -> next;
}
};
//有的链表有头结点,有的链表没有头结点,一般的单链表都存在头结点,头节点不存放数据,链表可以有头结点
也可以没有头节点,但不能没有头指针,链表名一般为头指针。
4.给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
解法1:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target,或者数组中没有,把target插入到nums中
return middle;
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;这时候middle=left=right,返回谁都一样
// 目标值插入数组中的位置 [left, right],例如数组[1,3,5,8],目标值是4,第一次middle是1
nums[left]<target,所以left会加1,加完后middle=left=2,这时候nums[mid]>target,循环还在继续,right=mid-1=1,
这时候right<left,循环直接退出了,直接返回right+1;
// 目标值在数组所有元素之后的情况 [left, right], return right + 1
return right + 1;
}
};
二分查找重点概括
写成 while(left < right) ,退出循环的时候有 left == right 成立,好处是不用判断应该返回 left 还是 right;
区间 [left..right] 划分只有以下两种情况:
分成 [left..mid] 和 [mid + 1..right],分别对应 right = mid 和 left = mid + 1;
分成 [left..mid - 1] 和 [mid..right],分别对应 right = mid - 1 和 left = mid,这种情况下。需要将 int mid = (left + right) / 2 改成 int mid = (left + right + 1) / 2,
否则会出现死循环,这一点不用记,出现死循环的时候,把 left 和 right 的值打印出来看一下就很清楚了;
退出循环 left == right,如果可以确定区间 [left..right] 一定有解,直接返回 left 就可以。否则还需要对 left 这个位置单独做一次判断;
二分查找的循环不变量是:在区间 [left..right] 里查找目标元素。
一、
二分法查找边界条件,当区间是[0,n)的时候,循环条件是while(left<right),其左值可以去到,右值取不到,当nums[middle]>target或者nums[middle]<traget时,
中值middle一定要被舍弃,当nums[middle]>target,有两种情况1.left=middle,这时候区间可能没有收缩,中值可能等于左值
2.left=middle+1,这时候区间必定收缩;当nums[middle]<traget时候,左区间不用管,右区间可能的情况有1.right=middle,这时候 right是娶不到的,区间已经收缩,
2.right=middle-1,middle没取到,middle-1的值也取不到,就要舍弃显然不合理。综上,这时候的区间left=middle+1,right=middle 上述情况都不满足的时候target就是中值,
当left=right时,是循环退出的时候,这时候,但right取不到,所以这时候就应该返回右边界的值,即返回right即可
二、二分法查找边界条件,当区间是[0,n-1]的时候,循环条件是while(left<right),左右值都可取到,这时候当nums[middle]>target,分两种情况1.left=middle,中值可能等于左值,
区间不一定会收缩,2.left=middle+1,区间一定收缩;当nums[middle]<traget,分两种情况,1.right=middle,中值可能等于左值,区间可能没收缩;2.right=middle-1,区间一定收缩。
综上,left=middle+1,right=middle-1;最后返回的就是中值,返回middle;若目标值在数组中没有,但在数组的范围内或大于整个数组,则返回right+1,
解法2:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n; // 定义target在左闭右开的区间里,[left, right) target
while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间
int middle = left + ((right - left) >> 1);
if (nums[middle] > target) {
right = middle; // target 在左区间,在[left, middle)中
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,在 [middle+1, right)中
} else { // nums[middle] == target
return middle; // 数组中找到目标值的情况,直接返回下标
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0,0)
// 目标值等于数组中某一个元素 return middle
// 目标值插入数组中的位置 [left, right) ,return right 即可
// 目标值在数组所有元素之后的情况 [left, right),return right 即可
return right;
}
};
5.
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并「原地」修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
解法1:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因为下表i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
}
};
注意点:注意解题逻辑,逻辑一定要清楚,两层for循环,元素覆盖法,解题逻辑:元素肯定要遍历一遍,一个for循环
如果发现了有与目标值(if判断),就要挨个覆盖元素,又一个for循环,最后是覆盖元素,中间的两个for循环的起始值
是有关联的,第一个for循环是遍历,第二个for循环是覆盖,外层for循环指向元素的第一个位置,那么内层for循环
就得指向第二个元素,因为第二个元素随时等着覆盖第一个元素呢,覆盖若有,外层for循环的指向还是原来地址的元素
所以得执行i--,元素的总个数也得减1,n--;最后返回元素的个数n。
6.字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
?
示例 1:
输入: s = "abcdefg", k = 2
输出:?"cdefgab"
示例 2:
输入: s = "lrloseumgh", k = 6
输出:?"umghlrlose"
解法1:
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(),s.end());
reverse(s.begin(),s.begin()+s.size()-n);
reverse(s.begin()+s.size()-n,s.end());
return s;
}
};
解法二:
三次翻转:
class Solution {
private:
void reverse(string&s,int left,int right)
{
while(left<=right)
{
swap(s[left],s[right]);
++left;
--right;
}
}
public:
string reverseLeftWords(string s, int n) {
int num = s.size()-1;
reverse(s,0,num);
reverse(s,0,num-n);
reverse(s,num-n+1,num);
return s;
}
};
解法:3
class Solution {
public:
string reverseLeftWords(string s, int n) {
int len = s.size();
s += s;
return s.substr(n,len);
}
};
解法四:
class Solution {
public:
string reverseLeftWords(string s, int n) {
if (n >= s.length() || s.length() < 2 || n == 0) {
return s;
}
int i = 0, count = 0;
while (count < n) {
char temp = s[i];
s += temp;
s.erase(i, 1);
count++;
}
return s;
}
};
7.给字符串定义一个旋转操作,比如字符串ABCD,旋转一次变成BCDA,继续旋转则变成CDAB,DABC。
给定两个字符串“源”和“目标”,请判断“源”在旋转一定次数后是否可以包含目标。
输入:三组长度非空自字符串,一共6行,奇数行为“源”字符串,偶数行为“目标”字符串。
输出:每组字符串是否可以旋转包含,包含返回1,不包含返回0;
#include <iostream>
#include <string>
using namespace std;
class Solution {
public:
bool rotateString(string A, string B) {
if(A.size() != B.size()) return false;
if(A.size() == 0) return true;
int n = A.size();
for(int i = 0,j = 0; j < n; ++j) {// i --旋转次数 j -- 枚举位置(B)
if(A[(i + j) % n] != B[j]) {
if(i >= n) return false;
++i,j = 0;
}
}
return true;
}
int main()
{
string s[6];
Solution t;
for(int i=0;i<6;i++)
{
cin>>s[i];
}
bool ret1=Solution.rotateString(s[0],s[1]);
bool ret2=Solution.rotateString(s[2],s[3]);
bool ret1=Solution.rotateString(s[4],s[5]);
cout << ret1 << ret2 << ret3 << endl;
system("system pause");
return 0;
}
力扣题目与解析
于 2022-01-16 18:09:57 首次发布