LeetCode刷题笔记【中等】

Z 字形变换

题目描述:
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:

L   C   I   R
E T O E S I I G
E   D   H   N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。

解题思路:

  • 当空字符或者只有一个字符的时候直接输出字符;

  • 按如图所示的颜色将字符分组

  • 每一组字符长度:num1=numRows*2-2,

  • 分组个数: num=s.size()/num1或者num=s.size()/num1+1

在这里插入图片描述

  • 按规律输出字符

代码实现:

string convert(string s, int numRows)
{
	string res;
	int groupl = numRows * 2 - 2;//groupl表示每个分组的长度
	if (s.size() < 2 || groupl == 0)
	{
		res = s;
		return res;
	}
	int num = s.size() / groupl + 1;//num表示分组个数
	if (s.size() < 2 || groupl == 0)
	{
		res = s;
		return res;
	}
	if (s.size() % groupl == 0)
	{
		num = s.size() / groupl;
	}
	// 首先进行分组,并将分组结果保存
	vector<string> vec(num);
	int k = 0;
	int count = 0;
	for (int i = 0; i < s.size(); ++i)
	{
		if (count == groupl)//表示一个组已经分完
		{
			count = 0;
			k++;
		}
		vec[k].push_back(s[i]);
		count++;
	}

	//将每个分组的首元素添加到结果中
	for (int i = 0; i < num; ++i)
	{
		res.push_back(vec[i][0]);
	}

	//处理其他元素
	for (int m = 1; m < numRows; ++m)//m:处理每一行
	{
		for (int n = 0; n < num; ++n)//n:处理每一组
		{
			if (m < vec[n].size())
			{
				res.push_back(vec[n][m]);
			}
			if ((groupl - m) < vec[n].size() && (groupl - m) != m)
			{
				res.push_back(vec[n][groupl - m]);
			}
		}
	}
	return res;
}

不同路径 II

问题描述:

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?


示例 1:

输入:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

解题思路:

动态规划

  • dp[i][j]表示从左上角开始到位置(i,j)的最短的路径
  • 对矩阵的第一行和第二行分别进行单独处理
  • 不为1则代表可达,否则置为0
  • 对于中间元素,当当前元素为0(表示可达)只取决于其左方和上方的路径
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

代码实现:

 int uniquePathsWithObstacles(vector<vector<int>>& grid)
{
	int row = grid.size();
	if (row == 0)
	{
		return -1;
	}
	int col = grid[0].size();
	//dp[i][j]表示从左上角开始到位置(i,j)的最短的路径
	vector<vector<long long>>  dp(row, vector<long long>(col));
	if (grid[0][0] == 1)
	{
		dp[0][0] = 0;
		return 0;
	}
	dp[0][0] = 1;
	for (int i = 1; i < col; ++i)//处理dp的第一行
	{
		if (grid[0][i] != 1 && dp[0][i - 1] == 1)
		{
			dp[0][i] = 1;
		}
		else
		{
			dp[0][i] = 0;
		}
	}
	for (int j = 1; j < row; ++j)//处理dp的第一列
	{
		if (grid[j][0] != 1 && dp[j-1][0] == 1)
		{
			dp[j][0] = 1;
		}
		else
		{
			dp[j][0] = 0;
		}
	}

	for (int i = 1; i < row; ++i)
	{
		for (int j = 1; j < col; ++j)
		{
			if (grid[i][j] != 1)    
			{
				dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
			}
			else
			{
				dp[i][j] = 0;
			}
		}
	}
	return dp[row - 1][col - 1];
}

重复的DNA序列

问题描述:

所有 DNA 由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。
编写一个函数来查找 DNA 分子中所有出现超多一次的10个字母长的序列(子串)。

示例:

输入: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"

输出: ["AAAAACCCCC", "CCCCCAAAAA"]

解题思路:
将所有长度为10的子串存放进map中,可根据map中的子串出现的次数查询出我们需要的子串。


代码实现:

 vector<string> findRepeatedDnaSequences(string s) 
        {
            int len = s.size();
            vector<string> res;
            unordered_map<string, int> mymap;
            if (len<10)
            {
                return res;
            }
            for (int i = 0; i < len; ++i)
            {
                string word = s.substr(i, 10);
                mymap[word]++;
            }
            auto it = mymap.begin();
            while (it != mymap.end())
            {
                if (it->second > 1)
                {
                    res.push_back(it->first);
                }
                it++;
            }
            return res;
        }

有效的数独

题目描述:

判断一个 9 × 9 9×9 9×9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。(数独部分空格内已填入了数字,空白格用 ‘.’ 表示。)

  • 数字 1 − 9 1-9 19 在每一行只能出现一次。
  • 数字 1 − 9 1-9 19 在每一列只能出现一次。
  • 数字 1 − 9 1-9 19$ 在每一个以粗实线分隔的 3 × 3 3×3 3×3 宫内只能出现一次。

在这里插入图片描述
示例 1:

输入:
[
  ["5","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]

输出: true


示例 2:

输入:
[
  ["8","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: false

解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
     但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的

解题思路:

  • 申请三个二维数组 r a w F l a g rawFlag rawFlag c o l F l a g colFlag colFlag以及 c e l l F l a g cellFlag cellFlag
  • r a w F a l g [ ] [ ] rawFalg[][] rawFalg[][]用于代表某行中是否出现过数字X:

假设:i = 0;c = 8;那么rawFlag[i][c] = true的含义为:在第0行中,出现过数字8。

假设:i = 8; c = 1;那么rawFlag[i][c] = true的含义为:在第8行中,出现过数字1。
  • c o l F l a g [ ] [ ] colFlag[][] colFlag[][]用于代表某列中是否出现过数字 X X X
假设:i = 0;c = 8;那么colFlag[i][c] = true的含义为:在第0列中,出现过数字8。

假设:i = 8; c = 1;那么colFlag[i][c] = true的含义为:在第8列中,出现过数字1。
  • c e l l F l a g [ ] [ ] cellFlag[][] cellFlag[][]的含义为某个 3 × 3 3×3 3×3方阵中是否出现过数字 X X X,此外,假设左上角是第一个方阵,右上角是第三个方阵,左下角是第六个方阵,右下角是第九个方阵。
假设:i = 0;c = 8;那么cellFlag[i][c] = true的含义为:在第0个方阵中,出现过数字8。

假设:i = 8; c = 1;那么cellFlag[i][c] = true的含义为:在第8个方阵中,出现过数字1
  • 使用 3 ∗ ( i / 3 ) + j / 3 3 * (i / 3) + j / 3 3(i/3)+j/3 ( i i i代表行的增加、 j j j代表列的增加)就可以根据当前的行列关系计算出当前处于第几个方阵。

代码实现

bool isValidSudoku(vector<vector<char> > &board) {
        if (board.empty() || board[0].empty()) return false;
        int m = board.size(), n = board[0].size();
        vector<vector<bool> > rowFlag(m, vector<bool>(n, false));//表示某行是否出现过该数字
        vector<vector<bool> > colFlag(m, vector<bool>(n, false));//表示某列是否出现过该数字
        vector<vector<bool> > cellFlag(m, vector<bool>(n, false));//表示某个3x3的矩阵中是否出现过该数字
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (board[i][j] >= '1' && board[i][j] <= '9') {
                    int c = board[i][j] - '1';
                    if (rowFlag[i][c] || colFlag[c][j] || cellFlag[3 * (i / 3) + j / 3][c]) 
                    {
                        //rowFlag[i][c]:表示i行是否出现过c
                        //colFlag[c][j]:表示j列是否出现过c
                        //cellFlag[i][c]:表示在第i个方阵中是否出现过数字c,其中该数字属于第几个方阵可通过其行列坐标计算得到
        
                        return false;
                    }
                    rowFlag[i][c] = true;
                    colFlag[c][j] = true;
                    cellFlag[3 * (i / 3) + j / 3][c] = true;
                }
            }
        }
        return true;
    }

搜索旋转排序数组 II

题目描述:

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。

示例 1:

输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true

示例 2:

输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false

解题思路:

  • 这道是之前那道 Search in Rotated Sorted Array 在旋转有序数组中搜索的延伸,现在数组中允许出现重复数字,这个也会影响我们选择哪半边继续搜索,由于之前那道题不存在相同值,我们在比较中间值和最右值时就完全符合之前所说的规律:如果中间的数小于最右边的数,则右半段是有序的,若中间数大于最右边数,则左半段是有序的

  • 而如果可以有重复值,就会出现来面两种情况,[3 1 1] 和 [1 1 3 1],对于这两种情况中间值等于最右值时,目标值3既可以在左边又可以在右边,那怎么办,对于这种情况其实处理非常简单,只要把最右值向左一位即可继续循环,如果还相同则继续移,直到移到不同值为止,然后其他部分还采用 Search in Rotated Sorted Array 在旋转有序数组中搜索中的方法。


代码实现:

 bool search(vector<int>& nums, int& target) {
        int n = nums.size();
        if (n == 0) return false;
        int left = 0, right = n - 1;
        while (left <= right) {//二分法,左右指针
            int mid = (left + right) / 2;
            if (nums[mid] == target) return true;
            else if (nums[mid] < nums[right]) {//中间值小于右边的值,则右半段是有序的
                if (nums[mid] < target && nums[right] >= target) left = mid + 1;
                else right = mid - 1;
            } else if (nums[mid] > nums[right]){//中间值大于左边的值,则左半段是有序的(同一升序空间)
                if (nums[left] <= target && nums[mid] > target) right = mid - 1;
                else left = mid + 1;
            } else --right;//中间值等于最右边的值时,左移右指针即可
        }
        return false;//没找到
    }

三角形最小路径和

题目描述:

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

例如,给定三角形:

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

解题思路:

动态规划

  • 从第二行开始,triangle[i][j] = min(triangle[i - 1][j - 1], triangle[i - 1][j]),
  • 两边的数字直接赋值上一行的边界值
    在这里插入图片描述

代码实现:

int minimumTotal(vector<vector<int>>& triangle) 
    {
        int len= triangle.size();

        vector<vector<int>> dp = triangle;
        for (int i = 1; i < len; ++i)
        {
            for (int j = 0; j <=i; ++j)
            {
                if (j == 0)//左边界
                {
                    dp[i][j] = dp[i - 1][j] + triangle[i][j];
                }
                else if (j == i)//右边界
                {
                    dp[i][j] = dp[i - 1][j - 1] + triangle[i][j];
                }
                else//中间部分
                {
                    dp[i][j] = min(dp[i - 1][j - 1] + triangle[i][j], dp[i - 1][j] + triangle[i][j]);
                }
            }
        }
        return *min_element(dp[len - 1].begin(), dp[len - 1].end());
    }

字母异位词分组

题目描述:

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

解题思路:

  • 我们发现如果把异位词的字符顺序重新排列,那么会得到相同的结果
  • 所以重新排序是判断是否互为错位词的方法
  • 由于错位词重新排序后都会得到相同的字符串,我们以此作为key,将所有错位词都保存到字符串数组中
  • 建立key和字符串数组之间的映射,最后再存入结果res中即可。

代码实现:

 vector<vector<string>> groupAnagrams(vector<string>& strs)
    {
        vector<vector<string>> res;//保存返回的结果
        unordered_map<string, vector<string>> mymap;//映射表
        int size = strs.size();
        if (size == 0)
        {
            return res;
        }
        for (string str : strs)
        {
            string  t = str;//先保存原始数据,防止排序后异位词的改变
            sort(t.begin(), t.end());//对每个异位词进行排序
            mymap[t].push_back(str);//以排序后的异位词作为键
        }
        auto it = mymap.begin();
        while (it != mymap.end())
        {
            res.push_back(it->second);
            it++;
        }
        return res;
    }

完全平方数

问题描述:

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例 1:

输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.

示例 2:

输入: n = 13
输出: 2
解释: 13 = 4 + 9.

解题思路:

  • d p [ n ] dp[n] dp[n] 表示以 n n n为和的最少平方的和的个数(所求)。

  • d p dp dp 数组所有下标已经为完全平方数的数(如 1 , 4 , 9... 1,4,9... 1,4,9...)置为 1 1 1

  • 动态方程的意思是:对于每个 i i i ,比 i i i 小一个完全平方数的那些数中最小的个数 + 1 +1 +1就是所求,也就是 d p [ i − j ∗ j ] + 1 dp [ i - j * j ] + 1 dp[ijj]+1


代码实现:

int numSquares(int n) 
    {
        vector<int> dp(n + 1, INT_MAX);
        for (int i = 1; i*i <= n; ++i)
        {
            if (i*i <= n)
            {
                dp[i*i] = 1;//每个完全平方数自身必然是由自身一个数组成
            }
        }

        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j*j <i ; ++j)//j*j为比i小的的完全平方数
            {
                dp[i] = min(dp[i], dp[i - j * j] + 1);
                if (dp[i] == 1)
                {
                    break;
                }
            }
        }
        return dp[n];
    }

优美的排列

题目描述:

假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。
条件:
第 i 位的数字能被 i 整除
i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?

示例1:

输入: 2
输出: 2
解释:1 个优美的排列是 [1, 2]:1 个位置(i=1)上的数字是11能被 i(i=1)整除
  第 2 个位置(i=2)上的数字是22能被 i(i=2)整除

第 2 个优美的排列是 [2, 1]:1 个位置(i=1)上的数字是22能被 i(i=1)整除
  第 2 个位置(i=2)上的数字是1,i(i=2)能被 1 整除

解题思路:

  • 对每一位从 1 − N 1-N 1N的数字进行判断,比如1 2 3 4 5 .
一号位可以放置:1 2 3 4 5.
二号位可以放置:1 2 4 
三号位只能放置:3  
四号位可以放置: 1 2 4 
  • 已经放置过的数字不能再使用了
  • 用变量 p o s pos pos来标记已经生成的数字的个数,如果大于N了,说明已经找到了一组排列,结果 r e s u l t result result自增 1 1 1
  • f o r for for循环中, i i i应该从 1 1 1开始,因为我们遍历 1 1 1 N N N中的所有数字,如果该数字未被使用过,且满足和坐标之间的整除关系,那么我们标记该数字已被访问过,再调用下一个位置的递归函数,之后不要忘记了恢复初始状态。

代码实现:

int countArrangement(int N) {
        int result = 0;
        vector<int> isIn(N + 1, 0);
        getAns(N, 1, result, isIn);//从一号位置开始
        return result;
    }
    void getAns(int N, int pos, int& result, vector<int>& isIn)
    {
        if(pos > N)
        {
            result ++;
            return;
        }
        else
        {
            for(int i = 1; i <= N; i ++)//对每一个数字进行判断
            {
                if(isIn[i] == 0 && (i % pos == 0 || pos % i == 0))
                {
                    isIn[i] = 1;//标记i这个数字被访问过
                    getAns(N, pos + 1, result, isIn);//调用下一个位置的递归函数
                    isIn[i] = 0;//恢复初始状态
                }
            }
        }
    }

字符串解码

题目描述:

给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3 a 3a 3a 2 [ 4 ] 2[4] 2[4] 的输入。

示例:

s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".

解题思路:

  • 使用两个栈(一个数字栈,一个字符串栈)
  • 遇到数字字符,就将数字字符转化为数字
  • 遇到’[’,就将数字和之前保存的栈顶字符串分别push进相应的栈中
  • 遇到字母字符,就不断进行累加到cur
  • 遇到’]’,首先将数字栈中的栈顶元素取出,用来作为循环入cur的次数

代码实现:

 string decodeString(string s) 
{
	//用两个栈,一个数字栈,一个字符串栈
	stack<int>  numstack;
	stack<string>  strstack;
	string cur = "";//保存每个中括号里的字符串
	int num = 0;
	for (int i = 0; i < s.size(); ++i)
	{
		if (s[i] >= '0'&&s[i] <= '9')
		{
			num = num * 10 + s[i] - '0';
		}
		if (s[i] == '[')//遇到左括号就将数字,字符入栈
		{
			numstack.push(num);
			strstack.push(cur);//首先将已经处理过的字符串push进字符串栈中
			cur.clear();//以便处理下一个未被处理过的字符串
			num = 0;
		}
		else if (s[i] >= 'a'&&s[i] <= 'z' || s[i] >= 'A'&&s[i] <= 'Z')
		{
			cur += s[i];
		}
		else if (s[i] == ']')
		{
			//取出数字栈中的栈顶元素,作为重复的次数
			int  k = numstack.top();
			numstack.pop();
			for (int i = 0; i < k; ++i)
			{
				strstack.top() += cur;
			}
			cur = strstack.top();//更新每次的cur为当前已经处理过的字符串
			strstack.pop();
		}	
	}
	return cur;
}

三数之和

题目描述:

给定一个包含 n n n 个整数的数组 n u m s nums nums,判断 n u m s nums nums 中是否存在三个元素 a a a b b b c c c ,使得 a + b + c = 0 ? a + b + c = 0 ? a+b+c=0找出所有满足条件且不重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4]

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

解题思路:

  • 先对数组进行从小到大升序排序
  • 之后用一个循环(循环范围 0~size-3)先确定一个数
  • 之后设立双指针头尾同时扫描数组右边剩下的数,如果找到两个数和为外层循环中已确定的相反数,那么存入解,并且去除 start 和 end 重复。
  • 对于外层循环,每一次需要去除从下标 i i i 开始的数字重复,一是为了去除重复答案。二是可以提高运行效率。

代码实现:

vector<vector<int>> threeSum(vector<int>& nums)
  {   
          vector<vector<int>> vec;//存放最终结果的数组
          vector<int> result;//每次存放一组符合条件的元素
           if(nums.size()<3)
           {
               return vec;
           }
           sort(nums.begin(),nums.end());//首先对数组进行排序

           int sum;
           for(int i=0;i<nums.size()-2;++i)
           {
               int start=i+1;
               int end=nums.size()-1;
               int k=-nums[i];//nums[i]为我们在外层循环确定好的一个数
              
               while(start<end)
               {
                   sum=nums[start]+nums[end];
                   if(sum==k)
                   {
                       result.push_back(nums[i]);
                       result.push_back(nums[start]);
                       result.push_back(nums[end]);
                       vec.push_back(result);
                       result.clear();
                       start++;
                       end--;
                       //去除start的重复
                       while(start<end&&nums[start]==nums[start-1])
                       {
                           start++;
                       }
                       //去除end的重复
                       while(start<end&&nums[end]==nums[end+1])
                       {
                           end--;
                       }
                   }
                   else if(sum<k)
                   {
                       start++;
                   }
                   else
                   {
                       end--;
                   }
               }
               //去除i的重复
               while(i<nums.size()-2&&nums[i]==nums[i+1])
               {
                   i++;
               }
               
           }
           return vec;
       
   }

最接近的三数之和

题目描述:

给定一个包括 n n n 个整数的数组 n u m s nums nums 和 一个目标值 t a r g e t target target。找出 n u m s nums nums 中的三个整数,使得它们的和与 t a r g e t target target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如,给定数组 nums = [-121-4], 和 target = 1.

与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

解题思路:
排序和双指针

  • 首先进行数组排序,时间复杂度 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
  • 在数组 n u m s nums nums 中,进行遍历,每遍历一个值利用其下标i,形成一个固定值 n u m s [ i ] nums[i] nums[i]
  • 再使用前指针指向 s t a r t = i + 1 start = i + 1 start=i+1 处,后指针指向 e n d = n u m s . l e n g t h − 1 end = nums.length - 1 end=nums.length1 处,也就是结尾处
  • 根据 s u m = n u m s [ i ] + n u m s [ s t a r t ] + n u m s [ e n d ] sum = nums[i] + nums[start] + nums[end] sum=nums[i]+nums[start]+nums[end] 的结果,判断 s u m sum sum 与目标 t a r g e t target target 的距离,如果更近则更新结果 r e s u l t result result
  • 同时判断 s u m sum sum t a r g e t target target 的大小关系
如果 sum > target 则 end--
如果 sum < target 则 start++
如果 sum == target 则说明距离为 0 直接返回结果
  • 整个遍历过程,固定值为 n 次,双指针为 n 次,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

  • 总时间复杂度: O ( n 2 ) O(n^2) O(n2)


代码实现:

 int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());//对数组进行排序
        int sum;
        int start;
        int end;
        int result = nums[0]+nums[1]+nums[2];
        for(int i=0;i<nums.size()-2;++i)
        {
            start=i+1;
            end=nums.size()-1;
            while(start<end)
            {
                sum=nums[i]+nums[start]+nums[end];
                if(abs(target-sum)<abs(target-result))
                {
                 //该结果更接近,则更新result为最接近的结果
                    result=sum;
                }
                if(target>sum)
                {
                   start++  ;
                }
                else
                {
                    end--;
                }
                
            }  
        }
        return result;
    }

四数之和

题目描述:

给定一个包含 n n n 个整数的数组 n u m s nums nums 和一个目标值 t a r g e t target target,判断 n u m s nums nums 中是否存在四个元素 a a a b b b c c c d d d ,使得 a + b + c + d a + b + c + d a+b+c+d的值与 t a r g e t target target 相等?找出所有满足条件且不重复的四元组。

示例:

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

解决思路:

  • 首先将数组从小到大排序
  • 之后先使用双层循环范围为 0 0 0~ s i z e − 3 size-3 size3的循环确定较小的两个数
  • 剩下的两个数通过设定两个指针头尾扫描右边循环没有遍历倒的数的
  • 在找到一个解之后,因为数组中的数字可能有重复,需要去重
  • 同样的对于外面的双层循环中,在每一次循环末尾也需要判断去重

代码实现:

vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        // 从小到大对数组进行排序
        sort(nums.begin(), nums.end());
        int size = nums.size();
        int curSum;
        int start, end;
        vector<int> save;
 
        for (int i = 0; i < size - 3; i++) {
            for (int j = i + 1; j < size - 2; j++) {
                curSum = nums[i] + nums[j];
                start = j + 1, end = size - 1;
                // 双指针扫描
                while (start < end) {
                    // 找到一个解
                    if (nums[start] + nums[end] == target - curSum) {
                        save.clear();
                        save.push_back(nums[i]);
                        save.push_back(nums[j]);
                        save.push_back(nums[start]);
                        save.push_back(nums[end]);
                        res.push_back(save);
                        start++;
                        end--;
                         // 去 start 重复
                        while (start < end && nums[start] == nums[start-1]) {
                            start++;
                        }
                       // 去 end 重复
                        while (start < end && nums[end] == nums[end+1]) {
                            end--;
                        }
                
                    } else if (nums[start] + nums[end] > target - curSum) {
                        end--;
               
                    } else {
                        start++;
                    }
                }
                 // 去 j 重复
                while (j < size - 2 && nums[j] == nums[j+1]) {
                    j++;
                }
            }
            // 去 i 重复
            while (i < size - 3 && nums[i] == nums[i+1]) {
                i++;
            }
        }
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值