找出数组中两个数刚好等于target的位置。【难度:低级】
法一:无脑遍历,时间复杂度O(n^2)
public int[] twoSum(int[] nums, int target) {
int[] result = new int[2];
int m = 0;
int n = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
m = i;
n = j;
break;
} else {
continue;
}
}
}
result[0] = m;
result[1] = n;
return result;
}
法二:官网给出的参考答案,使用HashMap记录了元素位置,时间复杂度O(n)
public int[] twoSumSolution3(int[] nums, int target) {
Map map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
小结: 尽管使用两层遍历结构也可以找出最终答案,但其牺牲了太多的时间。而官网给出的参考,以值为键,以位置为值,存放起来在Hash列表中,判断是否存在是只需要找出互补值,使用containsKey()进行判断,只需要一次遍历就可以找到答案,缩短了寻找耗时。
// 输入输出如下:
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
// 链表定义如下:
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
法一:做一个游标用以操作当前相关的数据
public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode result = new ListNode(0);// result记录了最开始的指针起点,不方便放到循环中运算
ListNode curr = result; // 要找一个做游标
int carry = 0;
while (l1 != null || l2 != null || carry != 0) { // 还有下个元素
int x = (l1 != null) ? l1.val : 0;
int y = (l2 != null) ? l2.val : 0;
int sum = carry + x + y;
carry = sum / 10;
curr.val = sum % 10;
l1 = (l1 != null) ? l1.next : null;
l2 = (l2 != null) ? l2.next : null;
if (l1 == null && l2 == null && carry == 0) {
break;
}
curr.next = new ListNode(0);
curr = curr.next;
}
return result;
}
官网示例:
public static ListNode addTwoNumbersSolution1(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode p = l1, q = l2, curr = dummyHead;
int carry = 0;
while (p != null || q != null) {
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
int sum = carry + x + y;
carry = sum / 10;
curr.next = new ListNode(sum % 10);
curr = curr.next;
if (p != null)
p = p.next;
if (q != null)
q = q.next;
}
if (carry > 0) {
curr.next = new ListNode(carry);
}
return dummyHead.next;
}
小结: 此题的难度在于如何使用列表将逆序链表作加,需要注意的是,此题在循环操作中需要使用另外一个ListNode进行遍历,操作完后,只要找到链表头结点作为返回即可。
输入输出示例如下:
"pwwkew", the answer is "wke"
参考示例:
public static int lengthOfLongestSubstringSolution2(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of
// character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
ans = Math.max(ans, j - i + 1);
map.put(s.charAt(j), j + 1);
}
return ans;
}
小结: 示例代码非常的简洁,这里依然是借助了HashMap的记忆功能,(以值为键,以位置为值),使用中需要特别注意containsKey和put方法的使用。
输入输出示例:
输入为:"PINALSIGYAHRPISKYTS",5
其锯齿模式如下:
第 【1】 行: P Y Y
第 【2】 行: I GA KT
第 【3】 行: N I H S S
第 【4】 行: A S R I
第 【5】 行: L P
返回为:PYYIGAKTNIHSSASRILP
这道题就是单纯的寻找规律即可,在求解中笔者将其分为“上坡”和“下坡”两个过程进行分析,找出每个字符与他下一个字符步长。也有博客提出了其他更简单的规律,学习一下,可以参考【这里】。
public static String convert(String s, int numRows) {
String result = "";
boolean isDown = true;// true表下坡,false表上坡
boolean isCenter = false;
if (numRows == 1) {
return s;
}
for (int i = 0; i < numRows; i++) {
int j = i;// 初始化j
int lenth = 0;
String line = "";
if (i == 0) {
isDown = true;
isCenter = false;
} else if (i == numRows - 1) {
isDown = false;
isCenter = false;
} else {
isDown = true;
isCenter = true;
}
System.out.print("第 【" + (i + 1) + "】 行:\t ");
while (j < s.length()) {
Character ch = s.charAt(j);
result = result + ch;
if (isDown) {
lenth = (numRows - (i + 1)) * 2;
} else {
lenth = i * 2;
}
line = line + ch;
int num = 0;
if (isCenter) {
num = isDown ? lenth / 2 : lenth / 2 - 1;
} else {
num = lenth / 2;
}
j = j + lenth;
if (j >= s.length())
break;
if (isCenter) {
isDown = isDown ? false : true; // 运行一次则反向
}
for (int k = 0; k < num; k++) {
line = line + " ";
}
}
System.out.println(line);
}
return result;
}
Reverse digits of an integer.
Example1: x = 123, return 321
Example2: x = -123, return -321
/**
* 返回回环数字
*
* @param x
* @return
*/
public static int reverse(int x) {
final int max = 0x7fffffff; // int最大值
final int min = 0x80000000; // int最小值
boolean isPositive = (x >= 0) ? true : false;
long result = 0;
while (x != 0) {
result = result * 10 + x % 10;
if (result > max || result < min) // 溢出处理
{
result = isPositive ? max : min;
return 0;
}
// 更新x
x /= 10;
}
return (int) result;
}
小结: 还是比较简单,但要注意对溢出情况的处理。
public static int myAtoi1(String str) {
final int INT_MAX = 0x7fffffff;
final int INT_MIN = 0x80000000;
boolean isPositive = true;
long result = 0;
str = str.trim();
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (i == 0 && ch == '-') {
isPositive = false;
continue;
}
if (i == 0 && ch == '+') {
continue;
}
if (i != 0 && (ch == '-' || ch == '+')) {
break;
}
int temp = ch - '0';
if ((temp > 10 || temp < 0) && ch != '-' && ch != '+') {
break;
// throw new IllegalArgumentException("出现了非法字符,无法进行转换!");
}
if (!isPositive)
temp = -temp;
result = (long) (result * 10 + temp);
if (!isPositive && result > 0) {
result = -result;
}
if (result > INT_MAX || result < INT_MIN) {
result = (result > 0) ? INT_MAX : INT_MIN;
break;
}
}
return (int) result;
}
/**
* 判断x是否为回文数字
*
* @param x
* @return
*/
public static boolean isPalindrome(int x) {
if (x < 0) {
return false;
}
int cnt = 0;
Map map = new HashMap();
while (x != 0) {
map.put(cnt, x % 10);
cnt++;// 统计数字位数
x /= 10;
}
for (int i = 0; i < cnt / 2; i++) {
if (map.get(i) != map.get(cnt - i - 1)) {
return false;
}
}
return true;
}
输入:
链表 = 1 --> 8 --> 3 --> 4,n=4
输出如下:
8 --> 3 --> 4
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
public static ListNode removeNthFromEnd(ListNode head, int n) {
ListNode myList[] = new ListNode[100];
ListNode dumyHead = new ListNode(0), p = dumyHead;
p.next = head;
int cnt = 0;
while (p != null) {
myList[cnt] = p;
p = p.next;
cnt++;
}
ListNode removeP = myList[cnt - n - 1]; // 找到需要删除的节点位置
removeP.next = removeP.next.next; // 删除节点
return dumyHead.next;
}
小结:新建一链表,将其next指向需求解的链表头部,遍历该链表,使用对象数组存储链表头部信息,遍历完成后通过数组信息进行节点删除操作。(ps:尽量使用原生态操作提升运行效率!)
主要判断三对括号的关闭顺序
The brackets must close in the correct order, "()" , "()[]{}" and "{[([])]}" are all valid but "(]" and "([)]" are not.
法一:采用递归进行求解
public static boolean isValid(String s) {
int len = s.length();
// 递归结束条件判断
if (len == 0) {
return true;
} else if (len == 1) {
return false;
} else {
while (len >= 2) {
for (int i = 0; i < len - 1; i++) {
// 括号匹配,则删掉递归
if (isBracketClose(s.charAt(i), s.charAt(i + 1))) {
s = s.substring(0, i) + s.substring(i + 2);// 删除对括号递归
return isValid(s);
}
}
if (len == s.length()) {
return false;
} else {
len = s.length();
}
}
return false;
}
}
/**
* 括号是否关闭
*
* @param left
* @param right
* @return
*/
public static boolean isBracketClose(char left, char right) {
if (left == '{' && right == '}') {
return true;
}
if (left == '[' && right == ']') {
return true;
}
if (left == '(' && right == ')') {
return true;
}
return false;
}
法二:利用堆栈先进后出的特性进行求解
代码待实现
小结:递归求解,主要要找出递推方式,并考虑递归结束条件。在求解过程中,如果关闭顺序合法,字符串中必然有一对括号是存在的,就可删掉这对括号进行递归。