力扣OJ(1-100)

目录

1. 两数之和

9. 回文数

11. 盛最多水的容器

15. 三数之和

19. 删除链表的倒数第N个节点 (链表的离散跳跃)

29. 两数相除

33. 搜索旋转排序数组

36. 有效的数独

42. 接雨水

45. 跳跃游戏 II

49. 字母异位词分组

51. N皇后

52. N皇后 II

55. 跳跃游戏

56. 合并区间

60. 第k个排列

63. 不同路径 II (二维DP)

64. 最小路径和

66. 加一

69. x 的平方根

70. 爬楼梯

74. 搜索二维矩阵

76. 最小覆盖子串

81. 搜索旋转排序数组 II

82. 删除排序链表中的重复元素 II

93. 复原IP地址

94. 二叉树的中序遍历

95. 不同的二叉搜索树 II

96. 不同的二叉搜索树

98. 验证二叉搜索树


1. 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

//给vector拓展,加上id并排序
template<typename T>
bool cmp(T x,T y)
{
    return x<y;
}
template<typename T>
vector<pair<T,int>> sortWithId(vector<T>v)
{
    vector<pair<T,int>>ans;
    ans.resize(v.size());
    for(int i=0;i<v.size();i++)ans[i].first=v[i],ans[i].second=i;
    sort(ans.begin(),ans.end(),[](pair<T,int>p1,pair<T,int>p2){return cmp(p1.first,p2.first);});
    return ans;      
}
//2个vector中寻找和为s的对,返回结果的每一行都是[id1,id2]
template<typename T>
vector<vector<int>> findSum(vector<T>v1,vector<T>v2,T s)
{
    vector<vector<int>>ans;
    vector<int>tmp(2);
    vector<pair<T,int>>v3=sortWithId(v2);
    sort(v2.begin(), v2.end(),cmp<T>);
    for(int i=0;i<v1.size();i++)
    {
        auto it1=lower_bound(v2.begin(), v2.end(), s-v1[i]);
        auto it2=upper_bound(v2.begin(), v2.end(), s-v1[i]);
        tmp[0]=i;
        for(auto j=it1;j<it2;j++)
        {
            tmp[1]=v3[j-v2.begin()].second;
            ans.push_back(tmp);
        }
    }
    return ans;
}
//删除二维vector中,含有重复元素的行
template<typename T>
vector<vector<T>> deleteSame(vector<vector<T>>&v)
{
    vector<vector<int>>ans;
    ans.reserve(v.size());
    for(int i=0;i<v.size();i++)
    {
        for(int j=0;j<v[i].size();j++)for(int k=j+1;k<v[i].size();k++)if(v[i][j]==v[i][k])goto here;
        ans.push_back(v[i]);
here:;
    }
    return ans;
}
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<vector<int>>ans=findSum(nums,nums,target);
        return deleteSame(ans)[0];
    }
};

9. 回文数

题目:

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

输入: 121
输出: true
示例 2:

输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:

输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:

你能不将整数转为字符串来解决这个问题吗?

代码:

class Solution {
public:
	bool isPalindrome(int x) {
		if (x < 0)return false;
		vector<int>num;
		while (x)
		{
			num.insert(num.end(), x % 10);
			x /= 10;
		}
		int len = num.size();
		for (int i = 0, j = len - 1; i < j; i++, j--)
		{
			if (num[i] - num[j])return false;
		}
		return true;
	}
};

11. 盛最多水的容器

题目:

给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例:

输入: [1,8,6,2,5,4,8,3,7]
输出: 49

代码:

class Solution {
public:
	int maxArea(vector<int>& height) {
		int i = 0, j = height.size() - 1;
		int ans = 0;
		while (i < j)
		{
			ans = max(ans, (j - i)*min(height[i], height[j]));
			if (height[i] < height[j])i++;
			else j--;
		}
		return ans;
	}
};

15. 三数之和

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

注意:答案中不可以包含重复的三元组。

示例:

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

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

经过各种优化之后,还是卡在了第311 / 313个用例,这个用例有3000个数。

//给vector拓展,加上id并排序
template<typename T>
bool cmp(T x,T y)
{
    return x<y;
}
template<typename T>
vector<pair<T,int>> sortWithId(vector<T>v)
{
    vector<pair<T,int>>ans;
    ans.resize(v.size());
    for(int i=0;i<v.size();i++)ans[i].first=v[i],ans[i].second=i;
    sort(ans.begin(),ans.end(),[](pair<T,int>p1,pair<T,int>p2){return cmp(p1.first,p2.first);});
    return ans;      
}
//2个vector中寻找和为s的对,返回结果的每一行都是[id1,id2]
template<typename T>
vector<vector<int>> findSum(vector<T>v1,vector<T>v2,T s)
{
    vector<vector<int>>ans;
    int m=min(int(v1.size()*v2.size()),1234567);
    ans.reserve(m);
    vector<int>tmp(2);
    vector<pair<T,int>>v3=sortWithId(v2);
    sort(v2.begin(), v2.end(),cmp<T>);
    for(int i=0;i<v1.size();i++)
    {
        auto it1=lower_bound(v2.begin(), v2.end(), s-v1[i]);
        auto it2=upper_bound(v2.begin(), v2.end(), s-v1[i]);
        tmp[0]=i;
        for(auto j=it1;j<it2;j++)
        {
            tmp[1]=v3[j-v2.begin()].second;
            ans.push_back(tmp);
        }
    }
    return ans;
}
//删除二维vector中,含有重复元素的行
template<typename T>
vector<vector<T>> deleteSame(vector<vector<T>>&v)
{
    vector<vector<int>>ans;
    ans.reserve(v.size());
    for(int i=0;i<v.size();i++)
    {
        for(int j=0;j<v[i].size();j++)for(int k=j+1;k<v[i].size();k++)if(v[i][j]==v[i][k])goto deleteSameHere;
        ans.push_back(v[i]);
deleteSameHere:;
    }
    return ans;
}
//枚举2个vector的两数之和
template<typename T>
vector<T> everySum(vector<T>&v1,vector<T>&v2)
{
    vector<T>ans;
    ans.resize(v1.size()*v2.size());
    int k=0;
    for(int i=0;i<v1.size();i++)for(int j=0;j<v2.size();j++)ans[k++]=v1[i]+v2[j];
    return ans;
}
//把id数组转化为对应的数v[id]
template<typename T>
vector<T> fgetNumFromId(vector<T> &v,vector<int>id)
{
    vector<T>ans;
    ans.resize(id.size());
    for(int i=0;i<id.size();i++)ans[i]= (id[i]>=0 && id[i]<v.size()) ? v[id[i]] : -1;
    return ans;
}
//二维id数组转化对应的数的二维数组
template<typename T>
vector<vector<T>> fgetNumFromId2(vector<T> &v,vector<vector<int>>&id)
{
    vector<vector<T>>ans; 
    ans.reserve(id.size());
    for(int i=0;i<id.size();i++)ans.push_back(fgetNumFromId(v,id[i]));
    return ans;
}
//二维数组每一行排序
template<typename T>
vector<vector<T>> sort2(vector<vector<T>>&v)
{
    for(int i=0;i<v.size();i++)sort(v[i].begin(),v[i].end());
    return v;
}
//判断2个vector是否全等
template<typename T>
bool isSame(vector<T>&v1,vector<T>&v2)
{
    if(v1.size()-v2.size())return false;
    for(int i=0;i<v1.size();i++)if(v1[i]!=v2[i])return false;
    return true;
}
//二维数组去掉重复行
template<typename T>
vector<vector<T>> deleteSameLine(vector<vector<T>>&v)
{
    vector<vector<T>>ans;
    ans.reserve(v.size());
    for(int i=0;i<v.size();i++)
    {
        for(int j=i+1;j<v.size();j++)if(isSame(v[i],v[j]))goto deleteSameLineHere;
        ans.push_back(v[i]);
deleteSameLineHere:;
    }
    return ans;
}
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        map<int,int>m;
        for(int i=0;i<nums.size();i++)m[nums[i]]++;
        vector<int>tmp(3);
        vector<vector<int>>ans2;
        tmp[0]=tmp[1]=tmp[2]=0;
        if(m[0]>=3)ans2.push_back(tmp);
        sort(nums.begin(),nums.end());
        for(int i=2;i<nums.size();i++)if(nums[i]==nums[i-2])nums.erase(nums.begin()+i--);
        for(int i=1;i<nums.size();i++)if(nums[i]==nums[i-1])
        {
            if(m[-nums[i]*2] && nums[i])tmp[0]=tmp[1]=nums[i],tmp[2]=-nums[i]*2,ans2.push_back(tmp);
            nums.erase(nums.begin()+i--);
        }
        vector<int>num2=everySum(nums,nums);
        vector<vector<int>>ans=findSum(num2,nums,0);
        for(int i=0;i<ans.size();i++)ans[i].push_back(ans[i][0]%nums.size()),ans[i][0]/=nums.size();
        ans=deleteSame(ans);
        ans=fgetNumFromId2(nums,ans);
        ans=sort2(ans);
        ans=deleteSameLine(ans);
        ans.resize(ans.size()+ans2.size());
        copy(ans2.begin(),ans2.end(),ans.end()-ans2.size());
        return ans;
    }
};

如果不是因为最近沉迷写小模板,其实根本不需要这么做的。

用三指针挪来挪去的方法更快。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        map<int,int>m;
        for(int i=0;i<nums.size();i++)m[nums[i]]++;
        vector<int>tmp(3);
        vector<vector<int>>ans,ans2;
        ans.reserve(nums.size()*nums.size());
        tmp[0]=tmp[1]=tmp[2]=0;
        if(m[0]>=3)ans2.push_back(tmp);
        sort(nums.begin(),nums.end());
        for(int i=2;i<nums.size();i++)if(nums[i]==nums[i-2])nums.erase(nums.begin()+i--);
        for(int i=1;i<nums.size();i++)if(nums[i]==nums[i-1])
        {
            if(m[-nums[i]*2] && nums[i])tmp[0]=tmp[1]=nums[i],tmp[2]=-nums[i]*2,ans2.push_back(tmp);
            nums.erase(nums.begin()+i--);
        }    
        for(int i=0;i<nums.size();i++)
        {
            for(int j=i+1,k=nums.size()-1;j<k;)
            {
                if(nums[i]+nums[j]+nums[k]>0)k--;
                else if(nums[i]+nums[j]+nums[k]<0)j++;
                else 
                {
                    tmp[0]=nums[i],tmp[1]=nums[j],tmp[2]=nums[k];
                    ans.push_back(tmp);
                    k--,j++;
                 }                   
            }
        }        
        ans.resize(ans.size()+ans2.size());
        copy(ans2.begin(),ans2.end(),ans.end()-ans2.size());
        return ans;
    }
};

19. 删除链表的倒数第N个节点 (链表的离散跳跃)

题目:

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

分析:

第一个想到的思路就是双指针,但是当n较小时,这个思路其实就是等同于扫描了2遍。

双指针一起扫描一遍和一个指针扫描2遍真的有区别吗?我认为没区别。

而我给出了基本上可以说是真正只扫描了一遍的算法:

链表的离散跳跃

当n比较小时,比如n<10,还是双指针,但是可以每次往前跳10步,一个指针要遍历10个节点,另外一个指针可以直接跳过去。

当n比较大时,比如n>100,每次跳10步的话,就需要保存很多中间指针,不可取。

我的算法是,如果n>100,那么每次跳n/10步,只需要保存11个指针即可。

注意,这里需要用到循环覆盖的技巧。

代码:

#define m 10
#define m2 100
typedef pair<ListNode*, int>thePair;
 
class Solution {
public:
	thePair move(ListNode* p, int k)
	{
		while (k && p)
		{
			k--,p = p->next;
		}
		return make_pair(p, k);
	}
	ListNode* removeNthFromEnd(ListNode* head, int n) {
		ListNode* p = head;
		ListNode* point[m + 1];//存跳跃点
		int nplus = n + m - n % m;
		int dif = max(nplus / m, m2);//单次跳跃距离
		int num = 0;//跳跃次数
		thePair pa;
		while (true)
		{
			pa = move(p, dif);
			if (pa.second > 0)break;
			point[num % (m + 1)] = pa.first;
			num++;
		}
		int dist = dif - pa.second;
		while (dist < n)
		{
			num = (num + m) % (m + 1);
			dist += dif;
		}
		ListNode* ans = point[num];
		if (num == 0)ans = head;
		if (dist == n)return head->next;
		while (dist > n+1)
		{
			ans = ans->next;
			dist--;
		}
		ans->next = ans->next->next;
		return head;
	}
};

29. 两数相除

题目:

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。

返回被除数 dividend 除以除数 divisor 得到的商。

示例 1:

输入: dividend = 10, divisor = 3
输出: 3
示例 2:

输入: dividend = 7, divisor = -3
输出: -2
说明:

被除数和除数均为 32 位有符号整数。
除数不为 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231,  231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。

代码:

class Solution {
public:
    int divide(int dividend, int divisor) {
        if(divisor==-1 && dividend==-2147483648 )return 2147483647;
        return dividend/divisor;
    }
};

33. 搜索旋转排序数组

题目:

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。

示例 1:

输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:

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

代码:

class Solution {
public:
	int search(vector<int>& nums, int target) {
		if (nums.empty())return -1;
		int low = 0, high = nums.size() - 1, mid;
		while (low < high)
		{
			mid = (high + low) / 2;
			if (nums[mid] == target)return mid;
			if (nums[mid] >= nums[low])
			{
				if (target >= nums[low] && target < nums[mid])high = mid - 1;
				else low = mid + 1;
			}
			else
			{
				if (target>nums[mid] && target <= nums[high])low = mid + 1;
				else high = mid - 1;
			}
		}
		if (nums[low] == target)return low;
		return -1;
	}
};

PS:

我在大三找实习的时候,面试一家公司做过这个题目。

当时我第一感觉就是O(n)的算法,面试官问我有没有O(log n)的算法,我简单说了大概思路,

面试官还要我把具体分类情况列出来,我没能成功。

其实不难,真的一点不难,但是面试稍微有点紧张,没有理清楚。

36. 有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

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


上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 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 存在, 因此这个数独是无效的。
说明:

一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 '.' 。
给定数独永远是 9x9 形式的。

class Solution {
public:
    bool ok(vector<vector<char>>& board, int i, int j, int k)
    {
        for (int jj = 0; jj < 9; jj++)if (jj!=j && board[i][jj] == k)return false;
        for (int ii = 0; ii < 9; ii++)if (ii!=i && board[ii][j] == k)return false;
        int x = i / 3 * 3, y = j / 3 * 3;
        for (int ii = x; ii < x + 3; ii++)for (int jj = y; jj < y + 3; jj++)
            if ((ii!=i||jj!=j) && board[ii][jj] == k)return false;
        return true;
    }
    bool isValidSudoku(vector<vector<char>>& board) {
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
                if(board[i][j]!='.' && !ok(board,i,j,board[i][j])){
                    return false;
                }
            }
        }
        return true;
    }
};

42. 接雨水

题目:

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。

示例:

输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6

代码:

class Solution {
public:
	int getMax(vector<int>& height, int i, int j)
	{
		int ansMax = -1, ans = -1;
		for (int k = i; k <= j; k++){
			if (ansMax < height[k]){
				ans = k, ansMax = height[k];
			}
		}
		return ans;
	}
	int getSum(vector<int>& height, int i,int j)
	{
		int ans = 0;
		for (int k = i; k <= j; k++)ans += height[k];
		return ans;
	}
	int trap(vector<int>& height,int i,int j) {
		if (j - i <= 1)return 0;
		int k = getMax(height, i + 1, j - 1);
		if (height[k] <= height[i] && height[k] <= height[j]){
			return min(height[i], height[j])*(j - i) + max(height[i], height[j]) - getSum(height, i, j);
		}
		return trap(height, i, k) + trap(height, k, j);
	}
	int trap(vector<int>& height) {
		if (height.size() < 2)return 0;
		return trap(height, 0, height.size() - 1);
	}
};

45. 跳跃游戏 II

题目:

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明:

假设你总是可以到达数组的最后一个位置。

代码:

class Solution {
public:
int jump(vector<int>& nums,int left,int right) {
        if(right>=nums.size()-1)return 0;
        int k=0;
        for(int i=left;i<=right;i++)k=max(k,i+nums[i]);
        return jump(nums,right+1,k)+1;
    }
    int jump(vector<int>& nums) {        
        return jump(nums,0,0);
    }
};

49. 字母异位词分组

题目:

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

示例:

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

所有输入均为小写字母。
不考虑答案输出的顺序。

思路:

逐个读取字符串,如果是第一次出现,就加入到一个新列表中,如果已经出现过在某个列表中,就加入这个列表的末尾。

由于一个列表中的字符串都是一样的,所以只要和每个列表的第一个字符串比较就行了。

但是,怎么比较两个字符串呢?

思路一:

计数,统计字母出现个数,从而比较2个字符串。

代码:

class Solution {
public:
	bool issame(string s1, string s2)
	{
		int num[26] = { 0 };
		for (int i = 0; i < s1.length(); i++)num[s1[i] - 'a']++;
		for (int i = 0; i < s2.length(); i++)num[s2[i] - 'a']--;
		for (int i = 0; i < 26; i++)if (num[i])return false;
		return true;
	}
	vector<vector<string>> groupAnagrams(vector<string>& strs) {
		vector<vector<string>> ans;
		for (int i = 0; i < strs.size(); i++)
		{
			string str = strs[i];
			bool flag = true;
			for (int i = 0; flag && i < ans.size(); i++)
			{
				if (issame(ans[i][0], str))
				{
					flag = false;
					ans[i].insert(ans[i].end(), str);
				}
			}
			if (flag)
			{
				vector<string>tmp;
				tmp.insert(tmp.end(), str);
				ans.insert(ans.end(), tmp);
			}
		}
		return ans;
	}
};

在最后一个用例超时了。

思路二:

把一个字符串映射到一个整数,比较两个字符串是否一样时只看是不是一个整数就行了。

同时,把每次映射的整数存到map里面,就不用一个个找,可以直接找到下标。

代码:

class Solution {
public:
	int change(string s)
	{
		int num[26] = { 0 }, ans = 0;
		long long k = 12345;
		for (int i = 0; i < s.length(); i++)num[s[i] - 'a']++;
		for (int i = 0; i < 26; i++)ans += num[i] * k, k = (k*k + 1) % 1234567890;
		return ans;
	}
	vector<vector<string>> groupAnagrams(vector<string>& strs) {
		vector<vector<string>> ans;
		map<int, int>m;
		int k = 0;
		for (int i = 0; i < strs.size(); i++)
		{
			int x = change(strs[i]);
			if (m[x]==0)
			{
				m[x] = ++k;
				vector<string>tmp;
				tmp.insert(tmp.end(), strs[i]);
				ans.insert(ans.end(), tmp);
			}
			else
			{
				ans[m[x]-1].insert(ans[m[x]-1].end(), strs[i]);
			}
		}
		return ans;
	}
};

51. N皇后

题目:

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

上图为 8 皇后问题的一种解法。

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例:

输入: 4
输出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。

代码:

int m;
int list_[100];
vector<vector<string>>ans;
 
void place(int row)
{
	if (row == m){
		string s;
		vector<string>res;
		for (int j = 0; j < m; j++){
			s = "";
			for (int i = 0; i < list_[j]; i++)s.append(".");
			s.append("Q");
			for (int i = list_[j]+1; i < m; i++)s.append(".");			
			res.insert(res.end(), s);
		}
		ans.insert(ans.end(), res);
		return;
	}
	for (int i = 0; i < m; i++)
	{
		bool flag = true;
		for (int j = 0; j < row; j++){
			if (list_[j] == i || list_[j] - j == i - row || list_[j] + j == i + row){
				flag = false;
			}
		}
		if (flag)
		{
			list_[row] = i;
			place(row + 1);
		}
	}
}
 
class Solution {
public:
	vector<vector<string>> solveNQueens(int n) {
		ans.clear();
		if (n <= 0)return ans;
		m = n;
		place(0);
		return ans;
	}
};

52. N皇后 II

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

上图为 8 皇后问题的一种解法。

给定一个整数 n,返回 n 皇后不同的解决方案的数量。

示例:

输入: 4
输出: 2
解释: 4 皇后问题存在如下两个不同的解法。
[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]

代码:

int m, sum;
int list_[100];
 
void place(int row)
{
	if (row == m)sum++;
	for (int i = 0; i < m; i++)
	{
		bool flag = true;
		for (int j = 0; j < row; j++)
			if (list_[j] == i || list_[j] - j == i - row || list_[j] + j == i + row)flag = false;
		if (flag)
		{
			list_[row] = i;
			place(row + 1);
		}
	}
}
 
class Solution {
public:
	int totalNQueens(int n) {
		m = n;
		sum = 0;
		place(0);
		return sum;
	}
};

55. 跳跃游戏

题目:

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

代码:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int i=0,k=0;
        while(i<nums.size() && i<=k)k = max(k,nums[i] + i++);
        return i==nums.size();
    }
};

56. 合并区间

题目:

给出一个区间的集合,请合并所有重叠的区间。

示例 1:

输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:

输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。

思路:

先排序,再按顺序依次合并即可

代码:

struct node{
	int a, b;
};
 
bool cmp(node n1,node n2){
	return n1.a < n2.a;
}
 
class Solution {
public:
	vector<vector<int>> merge(vector<vector<int>>& intervals) {		
		for (auto it = intervals.begin(); it != intervals.end(); ){
			if ((*it).size()<2 || (*it)[0] > (*it)[1])intervals.erase(it);
			else it++;
		}
		vector<vector<int>>ans;
		int size = intervals.size();
		if (intervals.empty())return ans;
		node *p = new node[size];
		for (int k = 0; k < size; k++){
			p[k].a = intervals[k][0], p[k].b = intervals[k][1];
		}
		sort(p, p + size, cmp);
		vector<int>tmp;
		for (int k = 0; k < size;){
			tmp.clear();
			tmp.insert(tmp.end(), p[k].a);
			tmp.insert(tmp.end(), p[k].b);
			k++;
			while (k < size){
				if (p[k].a <= tmp[1]){
					tmp[1] = max(tmp[1],p[k].b);
					k++;
				}
				else{
					break;
				}
			}
			ans.insert(ans.end(), tmp);
		}
		return ans;
	}
};

第二次做这个题目:

bool cmp(vector<int> x, vector<int> y)
{
    return x[0]<y[0];
}
vector<vector<int>> fmerge(vector<vector<int>>v)
{
    if(v.empty())return v;
    vector<vector<int>>ans;    
    sort(v.begin(),v.end(),cmp);
    vector<int>k,x;
    for(int i=0;i<v.size();i++)
    {
        x=v[i];
        if(k.empty())k=x;
        if(k[1]>=x[0])k[1]=max(k[1],x[1]);
        else
        {
            ans.push_back(k);
            k=x;
        }
    }
    ans.push_back(k);
    return ans;
}

60. 第k个排列

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。

说明:

给定 n 的范围是 [1, 9]。
给定 k 的范围是[1,  n!]。
示例 1:

输入: n = 3, k = 3
输出: "213"
示例 2:

输入: n = 4, k = 9
输出: "2314"

int listt[9] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320};
 
class Solution {
public:
string getPermutation(int n, int k) {
        vector<int>v(10,1);
        char c='0';
        string s="";
        for(int i=1;i<=9;i++)v[i]=i;
        for(int i=n-1;i>=0;i--)
        {
            int fac=listt[i];
            int r=(k+fac-1)/fac;
            s+=c+v[r];
            k-=(r-1)*fac;
            v.erase(v.begin()+r);
        }
        return s;
    }
};

63. 不同路径 II (二维DP)

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

说明:m 和 n 的值均不超过 100。

示例 1:

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

我的递归写法:

class Solution {
public:
    vector<vector<int>>ans;
    int dp(vector<vector<int>>& obs,int x,int y)
    {
        if(x<0||x>=obs.size()||y<0||y>=obs[0].size()||obs[x][y])return 0;
        if(ans[x][y])return ans[x][y];
        return ans[x][y]=dp(obs,x,y-1)+dp(obs,x-1,y);
    }
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        ans=obstacleGrid;
        for(int i=0;i<ans.size();i++)for(int j=0;j<ans[0].size();j++)ans[i][j]=0;
        ans[0][0]=1;
        return dp(obstacleGrid,obstacleGrid.size()-1,obstacleGrid[0].size()-1);
    }
};

我的非递归写法:

class Solution {
public:
    vector<vector<int>>ans;
    int uniquePathsWithObstacles(vector<vector<int>>& obs) {
        vector<vector<int>> ans=obs;
        ans[0][0]=(obs[0][0]?0:1);        
        for(int j=1;j<obs[0].size();j++)ans[0][j]=(obs[0][j]?0:ans[0][j-1]);
        for(int i=1;i<obs.size();i++){
            ans[i][0]=(obs[i][0]?0:ans[i-1][0]);
            for(int j=1;j<obs[0].size();j++)ans[i][j]=(obs[i][j]?0:ans[i][j-1]+ans[i-1][j]);
        }
        return ans[ans.size()-1][ans[0].size()-1];
    }
};

我的空间压缩写法:

class Solution {
public:
    vector<vector<int>>ans;
    int uniquePathsWithObstacles(vector<vector<int>>& obs) {
        vector<int> ans=obs[0];
        ans[0]=(obs[0][0]?0:1);        
        for(int j=1;j<obs[0].size();j++)ans[j]=(obs[0][j]?0:ans[j-1]);
        for(int i=1;i<obs.size();i++){
            ans[0]=(obs[i][0]?0:ans[0]);
            for(int j=1;j<obs[0].size();j++)ans[j]=(obs[i][j]?0:ans[j-1]+ans[j]);
        }
        return ans[ans.size()-1];
    }
};

64. 最小路径和

题目:

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例:

输入:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

代码:

class Solution {
public:
	int minPathSum(vector<vector<int>>& grid) {
		if (grid.empty())return -1;
		vector<int>ans1 = grid[0];
		for (int i = 1; i < ans1.size(); i++)
		{
			ans1[i] += ans1[i - 1];
		}
		vector<int>ans2;
		for (int i = 1; i < grid.size(); i++)
		{
			ans2 = grid[i];
			ans1[0] += ans2[0];
			for (int i = 1; i < ans1.size(); i++)
			{
				ans1[i] = min(ans1[i], ans1[i - 1]) + ans2[i];
			}
		}
		return ans1[ans1.size() - 1];
	}
};

66. 加一

题目:

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:

输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。

代码:
 

class Solution {
public:
	vector<int> plusOne(vector<int>& digits) {
		int key = 1;
		for (auto it = digits.end() - 1;key && it >= digits.begin(); it--)
		{
			*it += key, key--;
			key += (*it) / 10, *it %= 10;
		}
		if (key == 1)digits.insert(digits.begin(), 1);
		return digits;
	}
};

69. x 的平方根

题目:

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2
示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 
     由于返回类型是整数,小数部分将被舍去。

代码:

class Solution {
public:
    int mySqrt(int x) {
        return int(sqrt(x));
    }
};

70. 爬楼梯

题目:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶
示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

代码:

class Solution {
public:
	int climbStairs(int n) {
		if (n > 45)return 0;
		int list[46];
		list[0] = list[1] = 1;
		for (int i = 2; i <= n; i++)list[i] = list[i - 1] + list[i - 2];
		return list[n];
	}
};

74. 搜索二维矩阵

题目:

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:

输入:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 3
输出: true
示例 2:

输入:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 13
输出: false

代码:

class Solution {
public:
	bool searchMatrix(vector<vector<int>>& matrix, int target) {
		if (matrix.size() == 0)return false;
		if (matrix[0].size() == 0)return false;
		vector<int>head;
		for (auto it = matrix.begin(); it != matrix.end(); it++)
		{
			head.insert(head.end(), (*it)[0]);
		}
		auto it1 = lower_bound(head.begin(), head.end(), target);
		auto it2 = upper_bound(head.begin(), head.end(), target);
		if (it1 != it2)return true;
		if (it1 == head.begin())return false;
		head = matrix[it1 - head.begin() - 1];
		it1 = lower_bound(head.begin(), head.end(), target);
		it2 = upper_bound(head.begin(), head.end(), target);
		return it1 != it2;
	}
};

76. 最小覆盖子串

题目:

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例:

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:

如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:

首先记录t中每个字符出现的次数,比如假设字符A一共有3个,

然后扫描s,记录以当前字符结尾的包含3个A的最短子串长度,也就是说从这个字符往前第3个A的位置。

对于每个字符都有这么一个位置,即往前多少字符才有足够的字符,

取其中最大者就是所有字符都足够的最小长度。

实现方法一:只用数组

C语言AC代码:

#define MAX 1234567890
 
int getMin(int x[128])
{
	int ans = MAX;
	int i;
	for (i = 0; i < 128; i++){
		if (ans > x[i] && x[i] >= 0){
			ans = x[i];
		}
	}
	return ans;
}
 
char * minWindow(char * s, char * t){
	int numOfLetter[128];//numOfLetter表示每个字符的数量
	int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
	int startLoc, tempStartLoc;
	int length = MAX;
	bool haveLetter[128];//haveLetter表示每个字符是否出现过
	int sumOfLetter = 0;//一共有多少不同的字符
	int i;
	for (i = 0; i < 128; i++){
		numOfLetter[i] = 0;
		startLocOfLetter[i] = -1;
		haveLetter[i] = false;
	}
	char *tmp = t;
	while (*tmp != '\0'){
		numOfLetter[*tmp]++;  //初始化numOfLetter
		haveLetter[*tmp] = true;
		tmp++;
	}
	for (i = 0; i < 128; i++){
		if (haveLetter[i]){
			sumOfLetter++;
		}
	}
	for (i = 0; s[i] != '\0'; i++){
		if (!haveLetter[s[i]]){
			continue;
		}
		numOfLetter[s[i]]--;
		if (numOfLetter[s[i]] < 0 || startLocOfLetter[s[i]] < 0){
			while (true){
				startLocOfLetter[s[i]]++; //滑动startLocOfLetter
				if (s[startLocOfLetter[s[i]]] == s[i]){
					break;
				}
			}
		}
		if (numOfLetter[s[i]] == 0){
			sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
		}
		if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
			tempStartLoc = getMin(startLocOfLetter);
			if (length > i - tempStartLoc + 1){
				length = i - tempStartLoc + 1;
				startLoc = tempStartLoc;
			}
		}
	}
	if (length == MAX){
		length = 0;
	}
	static char *ans;
	ans = (char*)malloc(sizeof(char)*(length + 1));
	for (i = 0; i<length; i++){
		ans[i] = s[i + startLoc];
	}
	ans[length] = '\0';
	return ans;
}

C++语言AC代码:

#include<string>
#define MAX 1234567890
 
int getMin(int x[128])
{
	int ans = MAX;
	int i;
	for (i = 0; i < 128; i++){
		if (ans > x[i]&&x[i]>=0){
			ans = x[i];
		}
	}
	return ans;
}
 
class Solution {
public:
	string minWindow(string s, string t) {
		int numOfLetter[128];//numOfLetter表示每个字符的数量
		int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
		int startLoc, tempStartLoc;
		int length = MAX;
		bool haveLetter[128];//haveLetter表示每个字符是否出现过
		int sumOfLetter = 0;//一共有多少不同的字符
		int i;
		for (i = 0; i < 128; i++){
			numOfLetter[i] = 0;
			startLocOfLetter[i] = -1;
			haveLetter[i] = false;
		}
		for (i = 0; i < t.length(); i++){
			numOfLetter[t[i]]++;  //初始化numOfLetter
			haveLetter[t[i]] = true;
		}
		for (i = 0; i < 128; i++){
			if (haveLetter[i]){
				sumOfLetter++;
			}
		}
		for (i = 0; s[i] != '\0'; i++){
			if (!haveLetter[s[i]]){
				continue;
			}
			numOfLetter[s[i]]--;
			if (numOfLetter[s[i]] < 0 || startLocOfLetter[s[i]] < 0){
				while (true){
					startLocOfLetter[s[i]]++; //滑动startLocOfLetter
					if (s[startLocOfLetter[s[i]]] == s[i]){
						break;
					}
				}
			}
			if (numOfLetter[s[i]] == 0){
				sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
			}
			if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
				tempStartLoc = getMin(startLocOfLetter);
				if (length > i - tempStartLoc + 1){
					length = i - tempStartLoc + 1;
					startLoc = tempStartLoc;
				}
			}
		}
		if (length == MAX){
			return "";
		}
		string ans = s.substr(startLoc, length);
		return ans;
	}
};

实现方法二:利用C++的队列

C++语言AC代码:

#include<string>
#include<queue>
#define MAX 1234567890
 
class Solution {
public:
	string minWindow(string s, string t) {
		int numOfLetter[128];//numOfLetter表示每个字符的数量
		int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
		queue<int>loca[128];
		int startLoc,tempStartLoc;
		int length = MAX;
		bool haveLetter[128];//haveLetter表示每个字符是否出现过
		int sumOfLetter = 0;//一共有多少不同的字符
		int i;
		for (int i = 0; i < 128; i++){
			numOfLetter[i] = 0;
			startLocOfLetter[i] = -1;
			haveLetter[i] = false;
			while (!loca[i].empty()){
				loca[i].pop();
			}
		}
		for (i = 0; i < t.length(); i++){
			numOfLetter[t[i]]++;  //初始化numOfLetter
			haveLetter[t[i]] = true;
		}
		for (i = 0; i < 128; i++){
			if (haveLetter[i]){
				sumOfLetter++;
			}
		}
		for (i = 0; s[i] != '\0'; i++){
			if (!haveLetter[s[i]]){
				continue;
			}
			if (loca[s[i]].size() == numOfLetter[s[i]] - 1){
				sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
			}
			loca[s[i]].push(i);
			if (loca[s[i]].size() > numOfLetter[s[i]]){
				loca[s[i]].pop();//保持数量
			}
			if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
				tempStartLoc = MAX;
				for (int i = 0; i < 128; i++){
					if (haveLetter[i] && tempStartLoc > loca[i].front()){
						tempStartLoc = loca[i].front();
					}
				}
				if (length > i - tempStartLoc + 1){
					length = i - tempStartLoc + 1;
					startLoc = tempStartLoc;
				}
			}
		}
		if (length == MAX){
			return "";
		}
		string ans = s.substr(startLoc, length);
		return ans;
	}
};

实现方法三:利用C++的队列+堆

C++语言AC代码:

#include<string>
#include<set>
#include<queue>
#define MAX 1234567890
 
class Solution {
public:
	string minWindow(string s, string t) {
		int numOfLetter[128];//numOfLetter表示每个字符的数量
		int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
		queue<int>loca[128];
		set<int>sLoca;
		int startLoc;
		int length = MAX;
		bool haveLetter[128];//haveLetter表示每个字符是否出现过
		int sumOfLetter = 0;//一共有多少不同的字符
		int i;
		for (i = 0; i < 128; i++){
			numOfLetter[i] = 0;
			haveLetter[i] = false;
			while (!loca[i].empty()){
				loca[i].pop();
			}
		}
		sLoca.clear();
		for (i = 0; i < t.length(); i++){
			numOfLetter[t[i]]++;  //初始化numOfLetter
			haveLetter[t[i]] = true;
		}
		for (i = 0; i < 128; i++){
			if (haveLetter[i]){
				sumOfLetter++;
			}
		}
		for (i = 0; s[i] != '\0'; i++){
			if (!haveLetter[s[i]]){
				continue;
			}
			if (loca[s[i]].size() == numOfLetter[s[i]] - 1){
				loca[s[i]].push(i);
				sLoca.insert(loca[s[i]].front());
			}
			else{
				loca[s[i]].push(i);
			}
			if (loca[s[i]].size() > numOfLetter[s[i]]){
				sLoca.erase(loca[s[i]].front());
				loca[s[i]].pop();//保持数量
				sLoca.insert(loca[s[i]].front());
			}
			if (sumOfLetter == sLoca.size()){ //表示所有字母的个数已经足够
				if (length > i - *sLoca.begin() + 1){
					length = i - *sLoca.begin() + 1;
					startLoc = *sLoca.begin();
				}
			}
		}
		if (length == MAX){
			return "";
		}
		string ans = s.substr(startLoc, length);
		return ans;
	}
};

81. 搜索旋转排序数组 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

分析:

本题没有复杂度优于O(n)的算法。

代码:

class Solution {
public:
	bool search(vector<int>& nums, int target) {
		for (auto it = nums.begin(); it != nums.end(); it++)if (*it == target)return true;
		return false;
	}
};

82. 删除排序链表中的重复元素 II

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:

输入: 1->2->3->3->4->4->5
输出: 1->2->5
示例 2:

输入: 1->1->1->2->3
输出: 2->3

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        map<int,int>m;
        ListNode*p=head;
        while(p)m[p->val]++,p=p->next;
        p=head;
        while(p && m[p->val]>1)p=p->next;
        head=p;
        while(p)
        {
            while(p->next && m[p->next->val]>1)p->next=p->next->next;
            p=p->next;
        }
        return head;
    }
};

93. 复原IP地址

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 '.' 分隔。

示例:

输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]

class Solution {
public:
    vector<string> restoreIpAddresses(string s,int k) {
        vector<string>ans;
        if(s.length()==0)return ans;
        if(k>1)for(int i=0;i<3 && i<s.length()-1;i++)
        {
            string s1=s.substr(0,i+1),s2=s.substr(i+1,s.length()-i-1);
            int x=atoi(s1.c_str());
            if(x>255 || s[0]=='0' && s1.length()>1 || s1.length()>3)continue;
            vector<string>tmp=restoreIpAddresses(s2,k-1);
            for(int j=0;j<tmp.size();j++)
            {
                string s3=tmp[j];
                ans.push_back(s1+"."+s3);
            }
        }
        else
        {
            int x=atoi(s.c_str());
            if(x>255 || s[0]=='0' && s.length()>1 || s.length()>3);
            else ans.push_back(s);
        }
        return ans;
    }
    vector<string> restoreIpAddresses(string s) {
        return restoreIpAddresses(s,4);
    }
};

94. 二叉树的中序遍历

题目:

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
   1
    \
     2
    /
   3

输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?

代码:

/*
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
*/
 
class Solution {
public:
	vector<int> inorderTraversal(TreeNode* root) {
		vector<int>v1;
		if (root == NULL)return v1;
		v1 = inorderTraversal(root->left);
		v1.insert(v1.end(), root->val);
		vector<int>v2 = inorderTraversal(root->right);
		v1.insert(v1.end(), v2.begin(), v2.end());
		return v1;
	}
};

95. 不同的二叉搜索树 II

题目:

给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。

示例:

输入: 3
输出:
[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3
 

代码:

class Solution {
public:
	vector<TreeNode*> generateTrees(int low,int high) {
		vector<TreeNode*>ans;
		if (low > high)
		{
			ans.insert(ans.end(), NULL);
			return ans;
		}
		if (low == high)
		{
			TreeNode *p = new TreeNode(low);
			ans.insert(ans.end(), p);
			return ans;
		}
		for (int i = low; i <= high; i++)
		{
			vector<TreeNode*>v1 = generateTrees(low, i - 1);
			vector<TreeNode*>v2 = generateTrees(i + 1, high);
			for (int k = 0; k < v1.size(); k++)
			{
				for (int j = 0; j < v2.size(); j++)
				{
					TreeNode *p = new TreeNode(i);
					p->left = v1[k], p->right = v2[j];
					ans.insert(ans.end(), p);
				}
			}
		}
		return ans;
	}
	vector<TreeNode*> generateTrees(int n) {
		vector<TreeNode*>ans;
		if (n <= 0)return ans;
		return generateTrees(1, n);
	}
};

96. 不同的二叉搜索树

题目:

给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

示例:

输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

思路:

递推式f(n)=f(n-1)+f(1)f(n-2)+...+f(n-2)f(1)+f(n-1)

代码:

class Solution {
public:
	int numTrees(int n) {
		if (n <= 0)return 1;
		static map<int, int>ans;
		if (ans[n])return ans[n];
		int res = 0;
		for (int i = 0; i < n; i++)res += numTrees(i)*numTrees(n - 1 - i);
		return ans[n] = res;
	}
};

98. 验证二叉搜索树

题目:

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:

输入:
    2
   / \
  1   3
输出: true
示例 2:

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4 。

代码:

class Solution {
public:
	bool isValidBST(TreeNode* root, long long low, long long high){
		if (!root)return true;
		if (root->val <= low || root->val >= high)return false;
		return isValidBST(root->left, low, root->val) && isValidBST(root->right, root->val, high);
	}
	bool isValidBST(TreeNode* root) {		
		return isValidBST(root, -3312345678, 3312345678);
	}
};

猜你喜欢

转载自blog.csdn.net/nameofcsdn/article/details/113124721
今日推荐