leetcode刷题
5.最长回文子串
- 给你一个字符串 s,找到 s 中最长的回文子串。
原题链接
class Solution {
public String longestPalindrome(String s) {
if(s.length() == 1 || s.length() == 0){
return s;
}
int end = 0,start = 0;
for (int i = 0; i < s.length() ; i++) {
//奇数对称
int len1 = expandCenter(s,i,i);
//偶数对称
int len2 = expandCenter(s,i,i+1);
//取最大值
int len = Math.max(len1,len2);
//如果新的回文字符串长度大于之前的回文字符串长度,更新字符串的start和end
if(len > end - start){
start = i - (len - 1)/2;
end = i + len/2;
}
}
//所取区间[start,end]
return s.substring(start,end+1);
}
//计算回文串长度
private int expandCenter(String s,int right,int left){
while(left >= 0 && right < s.length() && s.charAt(right) == s.charAt(left)){
right++;
left--;
}
return right - left - 1;
}
}
11. 盛最多水的容器
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
原题链接
class Solution {
public int maxArea(int[] height) {
int startI = 0,endI = height.length -1;
int num[] = new int[height.length];
int i = 0;
while (startI < endI){
num[i] = Math.min(height[startI],height[endI]) * (endI-startI);
if(height[startI] < height[endI]){
startI++;
}else{
endI--;
}
i++;
}
int max = num[0];
for (int j = 1; j < num.length; j++) {
if(num[j] > max){
max = num[j];
}
}
return max;
}
}
回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
- 时间复杂度为log n
class Solution {
public boolean isPalindrome(int x) {
// 特殊情况:
// 如上所述,当 x < 0 时,x 不是回文数。
// 同样地,如果数字的最后一位是 0,为了使该数字为回文,
// 则其第一位数字也应该是 0
// 只有 0 满足这一属性
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revertedNumber = 0;
while (x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
// 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
// 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
// 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
return x == revertedNumber || x == revertedNumber / 10;
}
}
6、 Z 字形变换
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。
class Solution {
public String convert(String s, int numRows) {
if(numRows == 1){
return s;
}
//表示第几行
StringBuilder builder[] = new StringBuilder[numRows];
for (int i = 0; i < builder.length ; i++) {
builder[i] = new StringBuilder(10);
}
int col = numRows + numRows - 2;
char ca[] = s.toCharArray();
for (int i = 0; i < s.length() ; i++) {
int k = i % col;
if(k < numRows){
//Z字形的一竖
builder[k].append(ca[i]);
}else{
//Z字形的斜线
int l = numRows-(k - numRows) - 2;
builder[l].append(ca[i]);
}
}
StringBuilder ss = builder[0];
for (int i = 1; i < builder.length; i++) {
ss.append(builder[i]);
}
return ss.toString();
}
}
###15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums.length < 3){
return Collections.emptyList();
}
List<List<Integer>> ans = new ArrayList<>();
int len = nums.length;
Arrays.sort(nums);
for (int i = 0; i < len; i++) {
if(nums[i] > 0) break;
if(i > 0 && nums[i] == nums[i-1]) continue;
int L = i + 1;
int R = len - 1;
while (L < R){
int sum = nums[i] + nums[L] + nums[R];
if(sum == 0){
ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
while (L < R && nums[L] == nums[L+1]) L++;
while (L < R && nums[R] == nums[R-1]) R--;
L++;
R--;
}else if( sum < 0){
L++;
}else{
R--;
}
}
}
return ans;
}
}
12. 整数转罗马数字
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
//法一:自己写的真菜
class Solution {
public String intToRoman(int num) {
int x = num;
List<Integer> list = new ArrayList<>();
String s = "";
int cout = 0;
while(x != 0){
int i = x % 10;
if(cout == 0){
if(i > 0 && i < 4){
for (int j = i; j > 0 ; j--) {
s = "I" + s;
}
}
if(i > 4 && i < 9){
for (int j = i; j > 5 ; j--) {
s = "I"+ s;
}
s = "V" + s;
}
if(i == 4){
s = "IV" + s;
}
if(i == 9){
s = "IX" + s;
}
}else if(cout == 1){
if(i > 0 && i < 4){
for (int j = i; j > 0 ; j--) {
s = "X" + s;
}
}
if(i > 4 && i < 9){
for (int j = i; j > 5 ; j--) {
s = "X" + s;
}
s = "L" + s;
}
if(i == 4){
s = "XL" + s;
}
if(i == 9){
s = "XC" + s;
}
}else if(cout == 2){
if(i > 0 && i < 4){
for (int j = i; j > 0 ; j--) {
s = "C" + s;
}
}
if(i > 4 && i < 9){
for (int j = i; j > 5 ; j--) {
s = "C" + s;
}
s = "D" + s;
}
if(i == 4){
s = "CD" + s;
}
if(i == 9){
s = "CM" + s;
}
}else{
for (int j = 0; j < i ; j++) {
s = "M" + s;
}
}
cout++;
x /= 10;
}
return s;
}
}
//法二:大神写的
public String intToRoman(int num) {
String[] thousands = {"", "M", "MM", "MMM"};
String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
return thousands[num / 1000] + hundreds[num % 1000 / 100] + tens[num % 100 / 10] + ones[num % 10];
}
17. 电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
class Solution {
public List<String> letterCombinations(String digits) {
Map<Character,String[]> map = new HashMap<>();
Queue<String> queue = new LinkedList<>();
map.put('2',new String[]{"a","b","c"});
map.put('3',new String[]{"d","e","f"});
map.put('4',new String[]{"g","h","i"});
map.put('5',new String[]{"j","k","l"});
map.put('6',new String[]{"m","n","o"});
map.put('7',new String[]{"p","q","r","s"});
map.put('8',new String[]{"t","u","v"});
map.put('9',new String[]{"w","x","y","z"});
char ca[] = digits.toCharArray();
for (int i = 0; i < ca.length ; i++) {
if(map.containsKey(ca[i])){
String str[] = map.get(ca[i]);
if(queue.isEmpty()){
for (int j = 0; j < str.length; j++) {
queue.offer(str[j]);
}
}else{
String tempStr = queue.element();
int len = tempStr.length();
while (queue.element().length() == len){
tempStr = queue.poll();
for (int j = 0; j < str.length ; j++) {
queue.offer(tempStr+str[j]);
}
// System.out.println(queue.toString());
}
}
}
}
return (List)queue;
}
}
20. 有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
//解法一
class Solution {
public boolean isValid(String s) {
while(s.contains("()")||s.contains("[]")||s.contains("{}")){
if(s.contains("()")){
s=s.replace("()","");
}
if(s.contains("{}")){
s=s.replace("{}","");
}
if(s.contains("[]")){
s=s.replace("[]","");
}
}
return s.length()==0;
}
}
//解法二
class Solution {
public boolean isValid(String s) {
Map<Character,Character> map = new HashMap<>();
map.put(')','(');
map.put(']','[');
map.put('}','{');
char ca[] = s.toCharArray();
int len = ca.length;
if(len % 2 == 1){
return false;
}
Deque<Character> stack = new LinkedList<>();
for (int i = 0; i < len ; i++) {
if(map.containsKey(ca[i])){
if(stack.isEmpty() || stack.peek() != map.get(ca[i])){
return false;
}
stack.pop();
}else{
stack.push(ca[i]);
}
}
return stack.isEmpty();
}
}
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
//思路:用新建一个链表,比较l1和l2元素的大小,谁小就移动这个数
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode(0);
ListNode cur = head;
while (l1 != null && l2 != null){
if(l1.val <= l2.val){
cur.next = l1;
cur = cur.next;
l1 = l1.next;
}else{
cur.next = l2;
cur = cur.next;
l2 = l2.next;
}
}
if (l1 == null){
cur.next = l2;
}else{
cur.next = l1;
}
return head.next;
}
}
22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
### 思路1:
当我们清楚所有 i<n 时括号的可能生成排列后,对与 i=n 的情况,我们考虑整个括号排列中最左边的括号。
它一定是一个左括号,那么它可以和它对应的右括号组成一组完整的括号 "( )",我们认为这一组是相比 n-1 增加进来的括号。
那么,剩下 n-1 组括号有可能在哪呢?
剩下的括号要么在这一组新增的括号内部,要么在这一组新增括号的外部(右侧)。
class Solution {
public List<String> generateParenthesis(int n) {
LinkedList<LinkedList<String>> res = new LinkedList<>();
LinkedList<String> list0 = new LinkedList<>();
list0.add("");
res.add(list0);
//特殊情况
if(n == 0){
return res.get(0);
}
LinkedList<String> list1 = new LinkedList<>();
list1.add("()");
res.add(list1);
for (int i = 2; i <= n ; i++) {
LinkedList<String> temp = new LinkedList<>();
for (int j = 0; j < i ; j++) {
//遍历两种情况
List<String> str1 = res.get(j);
List<String> str2 = res.get(i-1-j);
for (String s1:str1) {
for (String s2:str2) {
String e1 = "(" + s1 + ")" + s2;
temp.add(e1);
}
}
}
res.add(temp);
}
return res.get(n);
}
}
//大佬思路,妙
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<String>();
generate(res, "", 0, 0, n);
return res;
}
//count1统计“(”的个数,count2统计“)”的个数
public void generate(List<String> res , String ans, int count1, int count2, int n){
//递归出口
if(count1 > n || count2 > n) return;
if(count1 == n && count2 == n) res.add(ans);
if(count1 >= count2){
String ans1 = new String(ans);
generate(res, ans+"(", count1+1, count2, n);
generate(res, ans1+")", count1, count2+1, n);
}
//System.out.println(res.toString());
}
}
23. 合并K个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
//思路一:顺序合并
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists == null){
return null;
}
int count = 0;
for (int i = 0; i < lists.length ; i++) {
if(lists[i] != null){
break;
}
count++;
}
if(count == lists.length){
return null;
}
for (int i = 0; i < lists.length - 1; i++) {
lists[i+1] = mergeTwoLists(lists[i],lists[i+1]);
}
return lists[lists.length-1];
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode(0);
ListNode cur = head;
while (l1 != null && l2 != null){
if(l1.val <= l2.val){
cur.next = l1;
cur = cur.next;
l1 = l1.next;
}else{
cur.next = l2;
cur = cur.next;
l2 = l2.next;
}
}
if (l1 == null){
cur.next = l2;
}else{
cur.next = l1;
}
return head.next;
}
}
31. 下一个排列
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
思路:参考
class Solution {
public void nextPermutation(int[] nums) {
if(nums.length <= 1){
return;
}
int len = nums.length;
int index1=0,index2 = 1;
int flag = 0;
for (int i = len - 1; i > 0 ; i--) {
if(nums[i] > nums[i-1]){
index1 = i-1;
index2 = i;
flag = 1;
break;
}
}
if(flag == 1){
for (int i = len - 1; i >= index2; i--) {
if(nums[index1] < nums[i]){
int temp = nums[i];
nums[i] = nums[index1];
nums[index1] = temp;
//[.....)
Arrays.sort(nums,index2,len);
return;
}
}
}else{
int j = len - 1;
for (int i = 0; i <= j; i++) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
j--;
}
}
}
}
32. 最长有效括号
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
思路参考:第一条评论
class Solution {
//存储结果的类结构
static class Team{
private int index;
private char value;
Team(int index,char value){
this.index = index;
this.value = value;
}
public char getValue() {
return value;
}
public int getIndex() {
return index;
}
}
public static int longestValidParentheses(String s) {
LinkedList<Team> queue = new LinkedList<>();
char ca[] = s.toCharArray();
int len = ca.length;
boolean trueOrFalse[] = new boolean[len];
//结果数组
for (int i = 0; i < len; i++) {
trueOrFalse[i] = false;
}
if(len <= 1){
return 0;
}
//模拟栈
for (int i = 0; i < len; i++) {
if(!queue.isEmpty()){
if (queue.element().getValue() == '(' && ca[i] == ')'){
trueOrFalse[i] = true;
trueOrFalse[queue.element().getIndex()] = true;
queue.pollFirst();
continue;
}
}
queue.addFirst(new Team(i,ca[i]));
}
//寻找连续个true的数组元素的长度
int bestLen = 0;
int tryLen = 0;
for (int i = 0; i <len ; i++) {
if (trueOrFalse[i]){
tryLen++;
if(tryLen > bestLen){
bestLen = tryLen;
}
}else{
tryLen = 0;
}
}
return bestLen;
}
}
33. 搜索旋转排序数组
升序排列的整数数组 nums 在预先未知的某个点上进行了旋转(例如, [0,1,2,4,5,6,7] 经旋转后可能变为 [4,5,6,7,0,1,2] )。
请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
注意:时间复杂度要求logn
- 思路:二分查找(官方题解)
class Solution {
public int search(int[] nums, int target) {
return search(nums,target,0,nums.length-1);
}
public int search(int[] nums, int target,int startIndex,int endIndex) {
int midIndex = (startIndex + endIndex) / 2;
int leftStart = startIndex;
//防止溢出
int leftEnd = midIndex - 1 < startIndex ? startIndex : midIndex-1;
int rightStart = midIndex + 1 > endIndex ? endIndex : midIndex+1;
int rightEnd = endIndex;
//跳出条件
if(nums[midIndex] == target){
return midIndex;
}
if(startIndex == endIndex && nums[midIndex] != target){
return -1;
}
//二分查找子数组
if(nums[leftStart] <= nums[leftEnd] ){
if(target >= nums[leftStart] && target <= nums[leftEnd]){
return search(nums,target,leftStart,leftEnd);
}else{
return search(nums,target,rightStart,rightEnd);
}
}else{
if(target >= nums[rightStart] && target <= nums[rightEnd]){
return search(nums,target,rightStart,rightEnd);
}else{
return search(nums,target,leftStart,leftEnd);
}
}
}
}
34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
注:要求使用logn时间复杂度的算法
- 思路:先用二分查找,若找到等于target元素的数组值,在从这个下标向左向右移动,直到其元素不等于target;若没有找到,就返回【-1,-1】
class Solution {
public int[] searchRange(int[] nums, int target) {
return searchRange(nums,target,0,nums.length-1);
}
public int[] searchRange(int[] nums,int target,int startIndex,int endIndex){
int midIndex = (startIndex + endIndex) / 2;
//数组为空,返回【-1,-1】
if(nums.length == 0){
return new int[]{-1,-1};
}
//未查找到,返回【-1,-1】
if(startIndex == endIndex && nums[midIndex] != target){
return new int[]{-1,-1};
}
if(nums[midIndex] == target){
int index1 = midIndex;
int index2 = midIndex;
//防止index1越界
while (index1-1 >= 0){
if(nums[index1-1] == target){
index1--;
}else{
break;
}
}
//防止index2越界
while (index2+1 <= nums.length-1){
if(nums[index2+1] == target){
index2++;
}else{
break;
}
}
return new int[]{index1,index2};
}else if(nums[midIndex] > target){
//防止溢出,二分查找子数组
if(midIndex-1 >= startIndex){
return searchRange(nums,target,startIndex,midIndex-1);
}else{
return searchRange(nums,target,startIndex,startIndex);
}
}else{
//防止溢出,二分查找子数组
if(midIndex+1 <= endIndex){
return searchRange(nums,target,midIndex+1,endIndex);
}else{
return searchRange(nums,target,endIndex,endIndex);
}
}
}
}
39. 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
思路:回溯算法,dfs,递归算法
未减枝:
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new LinkedList<>();
List<Integer> combine = new LinkedList<>();
if(candidates.length == 0){
return res;
}
dfs(res,combine,target,candidates,0);
return res;
}
public void dfs(List<List<Integer>> res,List<Integer> combine,int target,int[] candidates,int index){
/是否还可以向右
if(index == candidates.length){
return;
}
//满足条件
if(target == 0){
res.add(new LinkedList<>(combine));
return;
}
//往右
dfs(res, combine, target, candidates, index+1);
//判断是否还可以向下
if(target - candidates[index] >= 0){
combine.add(candidates[index]);
//往下
dfs(res,combine,target-candidates[index],candidates,index);
combine.remove(combine.size()-1);
}
}
}
减枝:
class Solution{
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
if (candidates.length == 0) {
return res;
}
// 排序是剪枝的前提,必须先进行排序
Arrays.sort(candidates);
List<Integer> path = new LinkedList<>();
dfs(candidates, 0, target, path, res);
return res;
}
public void dfs(int[] candidates,int index,int target,List<Integer> path,List<List<Integer>> res){
if(index == candidates.length){
return;
}
if(target == 0){
res.add(new LinkedList<>(path));
return;
}
//避免重复解
for (int i = index; i < candidates.length ; i++) {
//减枝
if(target - candidates[i] < 0){
break;
}
path.add(candidates[i]);
dfs(candidates, i, target-candidates[i], path, res);
path.remove(path.size()-1);
}
}
}
739. 每日温度
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
- 思路:单调栈,应用栈的数据结构实现。
- 大佬思路(秒呀!):大佬思路
class Solution {
public int[] dailyTemperatures(int[] T) {
//代表栈
LinkedList<Integer> teamList = new LinkedList<>();
int answers[] = new int[T.length];
//初始化数组
for (int i = 0; i < answers.length ; i++) {
answers[i] = 0;
}
//一次遍历
for (int i = 0; i < T.length; i++) {
int temp = T[i];
//单调栈
while (!teamList.isEmpty() && temp > T[teamList.peek()]){
int preIndex = teamList.pop();
answers[preIndex] = i - preIndex;
}
teamList.push(i);
System.out.println(teamList.toString());
}
return answers;
}
}
42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
class Solution {
public int trap(int[] height) {
if (height.length==0){
return 0;
}
int len = height.length;
int left_max[] = new int[len];
int right_max[] = new int[len];
int ans = 0;
left_max[0] = height[0];
for (int i = 1; i < len ; i++) {
left_max[i] = Math.max(left_max[i-1],height[i]);
}
right_max[len-1] = height[len-1];
for (int i = len - 2; i >=0 ; i--) {
right_max[i] = Math.max(right_max[i+1],height[i]);
}
for (int i = 0; i < len ; i++) {
int one = Math.min(left_max[i],right_max[i]) - height[i];
ans += one;
}
return ans;
}
}
46. 全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
- key:回溯算法,dfs
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> answers = new ArrayList<>();
Deque<Integer> path = new ArrayDeque<>();
int len = nums.length;
boolean []used = new boolean[len];
//数字个数为零时
if(len == 0){
return answers;
}
//dfs入口
dfs(nums,len,answers,path,0,used);
return answers;
}
public void dfs(int []nums,int len,List<List<Integer>> answers,Deque<Integer> path,int count,boolean []used){
//递归出口,当生成一个path后,used数组全为true
if(count == len){
answers.add(new ArrayList<>(path));
return;
}
//循环遍历,将未用过的值加入path
for (int i = 0; i < len; i++) {
if(!used[i]){
path.addFirst(nums[i]);
used[i] = true;
dfs(nums,len,answers,path,count+1,used);
//回溯
path.removeFirst();
used[i] = false;
}
}
}
}
48. 旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
//先转置,再对称变换
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
//上半角,置换下半角(转置)
for (int i = 0; i < n-1; i++) {
for (int j = i+1; j < n ; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
for (int i = 0; i < matrix.length ; i++) {
int left = 0;
int right = matrix[i].length - 1;
while (left <= right){
int temp = matrix[i][left];
matrix[i][left] = matrix[i][right];
matrix[i][right] = temp;
left++;
right--;
}
}
}
}
647. 回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
大佬思路
解一(动态规划):
class Solution {
public int countSubstrings(String s) {
int len = s.length();
boolean [][]dp = new boolean[len][len];
int ans = 0;
for (int i = 0; i < len; i++) {
for (int j = 0; j <= i ; j++) {
if(s.charAt(i) == s.charAt(j) &&(i-j < 2 || dp[j+1][i-1])){
dp[j][i] = true;
ans++;
}
}
}
return ans;
}
}
解二(中心点算法):
//647. 回文子串
class Solution {
public int countSubstrings(String s) {
int count = 0;
int len = s.length();
for (int i = 0; i < len; i++) {
count += numAdd(i,i,s,len);
count += numAdd(i,i+1,s,len);
}
return count;
}
public int numAdd(int left,int right,String s,int len){
int count = 0;
while (left >= 0 && right < len && s.charAt(left) == s.charAt(right)){
count++;
left--;
right++;
}
return count;
}
stem.out.println(solution.countSubstrings("aaa"));
}
}
49. 字母异位词分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
思路:利用一个map,将字符串排序结果作为key,字符串添加到list里面,将map的value作为返回的结果。
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String,List<String>> map = new HashMap<>();
int len = strs.length;
if (len == 0){
return null;
}
for (int i = 0; i < len; i++) {
char []ca = strs[i].toCharArray();
Arrays.sort(ca);
String key = String.valueOf(ca);
//太过冗杂
// if(!map.containsKey(key)){
// List<String> list = new ArrayList<>();
// list.add(strs[i]);
// map.put(key,list);
// }else{
// List<String> list = map.get(key);
// list.add(strs[i]);
// map.put(key,list);
// }
List<String> list = map.getOrDefault(key,new ArrayList<String>());
list.add(strs[i]);
map.put(key,list);
}
return new ArrayList<>(map.values());
}
}
53. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
参考☞
key:动态规划,贪心算法
class Solution {
public int maxSubArray(int[] nums) {
int sum = 0;
int len = nums.length;
int max = nums[0];
for (int i = 0; i < len; i++) {
if(sum > 0){
sum += nums[i];
}else{
//sum <= 0,从该数开始重新计数
sum = nums[i];
}
max = Math.max(max,sum);
}
return max;
}
}
55. 跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
/*
思路启发:
想象你是那个在格子上行走的小人,格子里面的数字代表“能量”,你需要“能量”才能继续行走。
每次走到一个格子的时候,你检查现在格子里面的“能量”和你自己拥有的“能量”哪个更大,取更大的“能量”! 如果你有更多的能量,你就可以走的更远啦!~
*/
class Solution {
public boolean canJump(int[] nums) {
int len = nums.length;
if (len == 1){
return true;
}
int maxIndex = 0;
for (int i = 0; i < len - 1; i++) {
if(maxIndex < i){
return false;
}
maxIndex = Math.max(maxIndex,nums[i]+i);
if(maxIndex >= len - 1){
return true;
}
}
return false;
}
}
- 动态规划解法:dp[i] = max(dp[i-1],i+nums[i]);
class Solution {
public boolean canJump(int[] nums) {
int len = nums.length;
int en = nums[0];
int i=0;
while (i != len-1 && en != 0) {
en--;
i++;
if(en < nums[i]){
en = nums[i];
}
}
if(en == 0 && i != len-1){
return false;
}
return true;
}
}
56. 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
class Solution {
public int[][] merge(int[][] intervals) {
int len = intervals.length;
if(len == 0 || len == 1){
return intervals;
}
//将同一start排在一起
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] intervals1, int[] intervals2) {
return intervals1[0] - intervals2[0];
}
});
List<int[]> merged = new ArrayList<int[]>();
int start = intervals[0][0];
int end = intervals[0][28];
for (int i = 1; i < len; i++) {
if(start == intervals[i][0]){
end = intervals[i][29] > end ? intervals[i][30] : end;
}else if(start < intervals[i][0] && end >= intervals[i][0] && end < intervals[i][31]){
end = intervals[i][32];
}else if(intervals[i][0] > end){
int []kk = new int[2];
kk[0] = start;
kk[1] = end;
merged.add(kk);
start = intervals[i][0];
end = intervals[i][33];
}
//最后一个的处理
if(i == len - 1){
int []kk = new int[2];
kk[0] = start;
kk[1] = end;
merged.add(kk);
}
}
return merged.toArray(new int[merged.size()][34]);
}
}
62. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
key:动态规划(下一个状态受之前的状态的影响)
class Solution {
public int uniquePaths(int m, int n) {
int [][]dp = new int[m][n];
for (int i = 0; i < m; i++) {
dp[i][0] = 1;
}
for (int i = 0; i < n ; i++) {
dp[0][i] = 1;
}
for (int i = 1; i < m ; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}
64. 最小路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
类似于62. 不同路径,都是动态规划
class Solution {
public int minPathSum(int[][] grid) {
int col_len = grid.length;
int row_len = grid[0].length;
int [][]dp = new int[col_len][row_len];
dp[0][0] = grid[0][0];
for (int i = 1; i < col_len ; i++) {
dp[i][0] = grid[i][0] + dp[i-1][0];
}
for (int i = 1; i < row_len; i++) {
dp[0][i] = grid[0][i] + dp[0][i-1];
}
for (int i = 1; i < col_len; i++) {
for (int j = 1; j < row_len; j++) {
dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
}
}
return dp[col_len-1][row_len-1];
}
}
70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
key:动态规划
代码:
class Solution {
public int climbStairs(int n) {
if(n == 1 || n == 2){
return n;
}
int []dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n ; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
}
//减小空间复杂度(法二)
class Solution {
public int climbStairs(int n) {
if(n == 1){
return 1;
}
int first = 1;
int second = 2;
for (int i = 3; i <= n ; i++) {
int third = first + second;
first = second;
second = third;
}
return second;
}
}
72. 编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
示例 1:
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
key:动态规划
class Solution {
public int minDistance(String word1, String word2) {
int col_len = word1.length();
int row_len = word2.length();
char []startWords = word1.toCharArray();
char []endWords = word2.toCharArray();
int [][]dp = new int[col_len+1][row_len+1];
dp[0][0] = 0;
for (int i = 1; i <= col_len; i++) {
dp[i][0] = i;
}
for (int i = 1; i <= row_len ; i++) {
dp[0][i] = i;
}
for (int i = 1; i <= col_len ; i++) {
for (int j = 1; j <= row_len ; j++) {
dp[i][j] = 1 + ( Math.min(dp[i][j-1],dp[i-1][j]) > dp[i-1][j-1] ? dp[i-1][j-1] : Math.min(dp[i][j-1],dp[i-1][j]) );
//words数组比dp数组小1
if(startWords[i-1] == endWords[j-1]){
dp[i][j] = dp[i-1][j-1];
}
}
}
return dp[col_len][row_len];
}
}
75. 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
class Solution {
public void sortColors(int[] nums) {
int len = nums.length;
int start = 0;
for (int i = 0; i < len ; i++) {
if (nums[i] == 0){
if(start != i){
swap(nums,start,i);
}
start++;
}
}
if(start < len){
for (int i = 0; i < len ; i++) {
if (nums[i] == 1){
if(start != i){
swap(nums,start,i);
}
start++;
}
}
}
}
public void swap(int []nums,int index1,int index2){
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
76. 最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
思路参考:点这里
class Solution {
public String minWindow(String s, String t) {
int start = 0,end = 0;
int k_start = -1,k_end = -1;
Map<Character,Integer> sMap = new HashMap<>();
Map<Character,Integer> tMap = new HashMap<>();
char []sChar = s.toCharArray();
char []tChar = t.toCharArray();
for (int i = 0; i < tChar.length ; i++) {
char c = tChar[i];
tMap.put(c,tMap.getOrDefault(c,0) + 1);
}
while (end < s.length()){
char c = sChar[end];
sMap.put(c,sMap.getOrDefault(c,0) + 1);
while (check(sMap,tMap) && start <= end){
if(k_end == -1){
k_end = end;
k_start = start;
}
if(end - start < k_end - k_start){
k_end = end;
k_start = start;
}
sMap.put(sChar[start],sMap.get(sChar[start]) - 1);
start++;
}
end++;
}
return k_start == -1 ? "" : s.substring(k_start,k_end+1);
}
public boolean check(Map<Character,Integer> map1,Map<Character,Integer> map2){
for (char key : map2.keySet()) {
if(!map1.containsKey(key) || map1.get(key) < map2.get(key) ){
return false;
}
}
return true;
}
}
78. 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
需要重点理解,回溯算法和dfs
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<Integer> list = new ArrayList<>();
List<List<Integer>> lists = new ArrayList<>();
int len = nums.length;
dfs(0,nums,list,lists,len);
return lists;
}
public void dfs(int i,int []nums,List<Integer> list,List<List<Integer>> lists,int len){
if(i == len){
lists.add(new ArrayList<>(list));
return;
}
list.add(nums[i]);
dfs(i+1,nums,list,lists,len);
list.remove(list.size() -1);
dfs(i+1,nums,list,lists,len);
}
}
371. 两整数之和
不使用运算符 + 和 - ,计算两整数 a 、b 之和。
思路参考:点这里
示例 1:
输入: a = 1, b = 2
输出: 3
class Solution {
public:
int getSum(int a, int b) {
while (b != 0){
int c = (unsigned int)(a&b) << 1;
a = a ^ b;
b = c;
}
return a;
}
};
1015 德才论 (25 分)
来源于:PAT乙级
宋代史学家司马光在《资治通鉴》中有一段著名的“德才论”:“是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之小人。凡取人之术,苟不得圣人,君子而与之,与其得小人,不若得愚人。”
现给出一批考生的德才分数,请根据司马光的理论给出录取排名。
输入格式:
输入第一行给出 3 个正整数,分别为:N(≤10^5),即考生总数;L(≥60),为录取最低分数线,即德分和才分均不低于 L 的考生才有资格被考虑录取;H(<100),为优先录取线——德分和才分均不低于此线的被定义为“才德全尽”,此类考生按德才总分从高到低排序;才分不到但德分到线的一类考生属于“德胜才”,也按总分排序,但排在第一类考生之后;德才分均低于H,但是德分不低于才分的考生属于“才德兼亡”但尚有“德胜才”者,按总分排序,但排在第二类考生之后;其他达到最低线的考生也按总分排序,但排在第三类考生之后。
随后 N 行,每行给出一位考生的信息,包括:准考证号 德分 才分,其中准考证号为 8 位整数,德才分为区间 [0, 100] 内的整数。数字间以空格分隔。
输出格式:
输出第一行首先给出达到最低分数线的考生人数 M,随后 M 行,每行按照输入格式输出一位考生的信息,考生按输入中说明的规则从高到低排序。当某类考生中有多人总分相同时,按其德分降序排列;若德分也并列,则按准考证号的升序输出。
原题链接
key:快排
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
struct Student{
int num;
int dScore;
int cScore;
int flag;
};
int comp(struct Student s1,struct Student s2){
// if s1 > s2 return 1
// else return 0;
if(s1.cScore+s1.dScore > s2.cScore+s2.dScore){
return 1;
}else if(s1.cScore+s1.dScore == s2.cScore+s2.dScore){
if(s1.dScore > s2.dScore){
return 1;
}else if(s1.dScore == s2.dScore){
if(s1.num < s2.num){
return 1;
}
}
}
return 0;
}
int findPos(struct Student *stu,int i,int j){
struct Student temp;
temp.cScore = stu[i].cScore;
temp.dScore = stu[i].dScore;
temp.num = stu[i].num;
while(i<j){
while(i<j && (comp(stu[j],temp) == 0)){
j--;
}
if(i<j){
stu[i].cScore = stu[j].cScore;
stu[i].dScore = stu[j].dScore;
stu[i].num = stu[j].num;
i++;
}
while(i<j && (comp(stu[i],temp) == 1)){
i++;
}
if(i<j){
stu[j].cScore = stu[i].cScore;
stu[j].dScore = stu[i].dScore;
stu[j].num = stu[i].num;
j--;
}
}
stu[i].cScore = temp.cScore;
stu[i].dScore = temp.dScore;
stu[i].num = temp.num;
return i;
}
void quickSort(struct Student *stu,int l,int r){
if(stu == NULL) return;
if(l < r){
int k = findPos(stu,l,r);
quickSort(stu,l,k-1);
quickSort(stu,k+1,r);
}
}
int main(int argc, char *argv[]) {
int n,low,high;
int i;
int count = 0;
scanf("%d %d %d",&n,&low,&high);
struct Student stu[n];
struct Student temp[n];
for(i=0;i<n;i++){
scanf("%d %d %d",&stu[i].num,&stu[i].dScore,&stu[i].cScore);
stu[i].flag = -1;
if(stu[i].dScore >= low && stu[i].cScore >=low){
stu[i].flag = 4;
}
if(stu[i].dScore >= high && stu[i].cScore >= high){
stu[i].flag = 1;
}
if(stu[i].dScore >= high && stu[i].cScore < high && stu[i].cScore>=low){
stu[i].flag = 2;
}
if(stu[i].dScore>=low && stu[i].dScore<high && stu[i].dScore >= stu[i].cScore && stu[i].cScore>=low){
stu[i].flag = 3;
}
}
for(i=0;i<n;i++){
if(stu[i].flag != -1){
count++;
}
}
int kk = 1;
printf("%d\n",count);
while(kk<=4){
int j = 0;
for(i=0;i<n;i++){
if(stu[i].flag == kk){
temp[j].cScore = stu[i].cScore;
temp[j].dScore = stu[i].dScore;
temp[j].num = stu[i].num;
j++;
}
}
if(temp != NULL){
quickSort(temp,0,j-1);
for(i=0;i<j;i++){
printf("%d %d %d\n",temp[i].num,temp[i].dScore,temp[i].cScore);
}
}
kk++;
}
return 0;
}
1017 A除以B (20 分)(大数除法)
本题要求计算 A/B,其中 A 是不超过 1000 位的正整数,B 是 1位正整数。你需要输出商数 Q 和余数 R,使得 A=B×Q+R 成立。
输入格式:
输入在一行中依次给出 A 和 B,中间以 1 空格分隔。
输出格式:
在一行中依次输出 Q 和 R,中间以 1 空格分隔。
原题链接
key:大数除法
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[]) {
char s[1000];
int q;
int i=0,j=0;
int k,l=0;
int kk=0;
scanf("%s %d",s,&q);
while(s[i] != '\0'){
k = s[i] - '0';
kk = kk*10+k;
if(kk<q){
if(i!=0){
printf("0");
}
i++;
continue;
}else{
l = kk / q;
printf("%d",l);
kk = kk % q;
}
i++;
}
//适用于一位数且 s<q
if(i==1 && l == 0){
printf("%d",l);
}
printf(" %d",kk);
return 0;
}
740. 删除并获得点数
给你一个整数数组 nums ,你可以对它进行一些操作。
每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。
示例 1:
输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。总共获得 6 个点数。
示例 2:
输入:nums = [2,2,3,3,3,4]
输出:9
解释:
删除 3 获得 3 个点数,接着要删除两个 2 和 4 。
之后,再次删除 3 获得 3 个点数,再次删除 3 获得 3 个点数。
总共获得 9 个点数。
原题链接
key:动态规划
- 方法一:1、将数组排序,合并相同元素,在空位填充0,运用打家劫舍方法(动态规划)
class Solution {
public int deleteAndEarn(int[] nums) {
//排序
int len = nums.length;
for (int i = 0; i < len-1; i++) {
int min = nums[i];
int minI = i;
for (int j = i; j < len; j++) {
if(nums[j] < min){
min = nums[j];
minI = j;
}
}
nums[minI] = nums[i];
nums[i] = min;
}
//合并与填充
int ans[] = new int[10000];
int k = 0;
ans[k] = nums[0];
for (int i = 1; i < len ; i++) {
if(nums[i] == nums[i-1]){
ans[k] += nums[i];
}else if(nums[i] != nums[i-1]+1){
//填充0
int l = nums[i-1],r = nums[i];
for (int j = l+1; j < r; j++) {
k++;
ans[k] = 0;
}
}
if(nums[i] != nums[i-1]){
k++;
ans[k] = nums[i];
}
}
//动态规划,打家劫舍
int ansLen = k+1;
if(ansLen == 1){
return ans[0];
}
if (ansLen == 2){
return ans[0]>ans[1]? ans[0]:ans[1];
}
int a1 = 0,a2 = ans[0],a3 = ans[1];
int temp = 0;
for (int i = 2; i < ansLen; i++) {
temp = (a1>a2? a1:a2) + ans[i];
a1 = a2;
a2 = a3;
a3 = temp;
}
return a2>a3? a2:a3;
}
}
- 方法二:记录0-10000的个数,找到nums[]的最大值max,max+1即为动态规划的边界,ans[i]*i即为这个点的收益,最后运用动态规划
class Solution {
public int deleteAndEarn(int[] nums) {
int len = nums.length;
int ans[] = new int[10001];
int max = nums[0];
for (int i = 0; i < len; i++) {
if(max < nums[i]){
max = nums[i];
}
ans[nums[i]]++;
}
//动态规划,打家劫舍
int ansLen = max+1;
if(ansLen == 1){
return 0;
}
if (ansLen == 2){
return ans[1];
}
int a1 = 0,a2 = 0,a3 = ans[1];
int temp;
for (int i = 2; i < ansLen; i++) {
temp = (a1>a2? a1:a2) + ans[i]*i;
a1 = a2;
a2 = a3;
a3 = temp;
}
return a2>a3? a2:a3;
}
}
918. 环形子数组的最大和
给定一个由整数数组 A 表示的环形数组 C,求 C 的非空子数组的最大可能和。
在此处,环形数组意味着数组的末端将会与开头相连呈环状。(形式上,当0 <= i < A.length 时 C[i] = A[i],且当 i >= 0 时 C[i+A.length] = C[i])
此外,子数组最多只能包含固定缓冲区 A 中的每个元素一次。(形式上,对于子数组 C[i], C[i+1], …, C[j],不存在 i <= k1, k2 <= j 其中 k1 % A.length = k2 % A.length)
示例 1:
输入:[1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3
示例 2:
输入:[5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10
示例 3:
输入:[3,-1,2,-1]
输出:4
解释:从子数组 [2,-1,3] 得到最大和 2 + (-1) + 3 = 4
示例 4:
输入:[3,-2,2,-3]
输出:3
解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3
示例 5:
输入:[-2,-3,-1]
输出:-1
解释:从子数组 [-1] 得到最大和 -1
原题链接
key:动态规划、环形问题
思路:
- 不使用环的情况时,直接通过53题的思路,逐步求出整个数组中的最大子序和即可
- 【重点】使用到了环,则必定包含 A[n-1]和 A[0]两个元素且说明从A1到A[n-2]这个子数组中必定包含负数【否则只通过一趟最大子序和就可以的=得出结果】,公式为sum-min(sum为数组和,min为最小序列和)
- 同时需要考虑数组元素全为负数的情况,这时sum-min = 0,而max<0,应该取max作为环形数组的最大和,而不是0
思路参考
class Solution {
public static void main(String []args){
Solution s = new Solution();
int a[] = {1,-2,3,-2};
System.out.println(s.maxSubarraySumCircular(a));
}
public int maxSubarraySumCircular(int[] nums) {
int len = nums.length;
if(len == 1){
return nums[0];
}
//最大和和最小和
int sumMax = nums[0],sumMin = nums[0];
int max = sumMax,min = sumMin;
//数组求和
int sum=nums[0];
for (int i = 1; i < len; i++) {
sum += nums[i];
if(sumMax > 0){
sumMax += nums[i];
}else{
sumMax = nums[i];
}
if(sumMin < 0){
sumMin += nums[i];
}else{
sumMin = nums[i];
}
if(sumMax > max){
max = sumMax;
}
if(sumMin < min){
min = sumMin;
}
}
if(sum == min)
return max;
return max > sum-min? max:sum-min;
}
}