环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
Java解法:
/*
哈希表
*/
/*
public boolean hasCycle(ListNode head) {
HashSet<ListNode> set = new HashSet<>();
while (head != null){
if (!set.contains(head)){
set.add(head);
}else{
return true;
}
head = head.next;
}
return false;
}
*/
/*
快慢指针
*/
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null){
return false;
}
ListNode low = head, fast = head.next;
while (low != fast){
if (fast == null || fast.next == null){
return false;
}
low = low.next;
fast = fast.next.next;
}
return true;
}
环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
Java解法:
/*
哈希表
*/
/*
public ListNode detectCycle(ListNode head) {
HashSet<ListNode> set = new HashSet<>();
while (head != null){
if (set.contains(head)){
return head;
}else{
set.add(head);
}
head = head.next;
}
return null;
}
*/
/*
快慢指针
*/
public ListNode detectCycle(ListNode head) {
if (head == null){
return null;
}
ListNode low = head , fast = head;
while (fast != null){
low = low.next;
if (fast.next != null){
fast = fast.next.next;
}else{
return null;
}
if (low == fast) {
ListNode ptr = head;
while (ptr != low) {
ptr = ptr.next;
low = low.next;
}
return ptr;
}
}
return null;
}
重排链表
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
Java解法:
/*
方法一:线性表
因为链表不支持下标访问,所以我们无法随机访问链表中任意位置的元素。
因此比较容易想到的一个方法是,我们利用线性表存储该链表,然后利用线性表可以下标访问的特点,直接按顺序访问指定元素,重建该链表即可。
*/
/*
public static void reorderList(ListNode head) {
if (head == null){
return;
}
List<ListNode> list = new ArrayList<>();
while (head != null){
list.add(head);
head = head.next;
}
int i = 0, j = list.size() - 1;
while (i < j){
list.get(i).next = list.get(j);
i++;
if (i == j){
break;
}
list.get(j).next = list.get(i);
j--;
}
// 最后赋 空
list.get(i).next = null;
}
*/
/*
方法二:寻找链表中点 + 链表逆序 + 合并链表
注意到目标链表即为将原链表的左半端和反转后的右半端合并后的结果。
这样我们的任务即可划分为三步:
1.找到原链表的中点
可以使用快慢指针来 地找到链表的中间节点。
2.将原链表的右半端反转
可以使用迭代法实现链表的反转。
3.将原链表的两端合并。
因为两链表长度相差不超过 11,因此直接合并即可。
*/
public static void reorderList(ListNode head) {
if (head == null) {
return;
}
ListNode midNode = findMidNode(head);
ListNode l1 = head , l2 = midNode.next;
midNode.next = null;
l2 = resverNode(l2);
mergeList(l1, l2);
}
/*
找链表中点,返回
*/
public static ListNode findMidNode(ListNode head){
ListNode low = head , fast = head;
while (fast.next != null && fast.next.next != null){
low = low.next;
fast = fast.next.next;
}
return low;
}
/*
反转链表
*/
public static ListNode resverNode(ListNode head){
ListNode prev = null;
ListNode cur = head;
while (cur != null){
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}
/*
合并
*/
public static void mergeList(ListNode l1, ListNode l2) {
ListNode tmp1, tmp2;
while (l1 != null && l2 != null){
tmp1 = l1.next;
tmp2 = l2.next;
l1.next = l2;
l1 = tmp1;
l2.next = l1;
l2 = tmp2;
}
}
对链表进行插入排序
Java解法:
/*
1.首先判断给定的链表是否为空,若为空,则不需要进行排序,直接返回。
2.创建哑节点 dummyHead,令 dummyHead.next = head。引入哑节点是为了便于在 head 节点之前插入节点。
3.维护 lastSorted 为链表的已排序部分的最后一个节点,初始时 lastSorted = head。
4.维护 curr 为待插入的元素,初始时 curr = head.next。
5.比较 lastSorted 和 curr 的节点值。
若 lastSorted.val <= curr.val,说明 curr 应该位于 lastSorted 之后,将 lastSorted 后移一位,curr 变成新的 lastSorted。
否则,从链表的头节点开始往后遍历链表中的节点,寻找插入 curr 的位置。令 prev 为插入 curr 的位置的前一个节点,进行如下操作,完成对 curr 的插入
6.令 curr = lastSorted.next,此时 curr 为下一个待插入的元素。
7.重复第 5 步和第 6 步,直到 curr 变成空,排序结束。
8.返回 dummyHead.next,为排序后的链表的头节点。
*/
public ListNode insertionSortList(ListNode head) {
if (head == null){
return head;
}
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode lastSorted = head, curr = head.next;
while (curr != null) {
if (lastSorted.val <= curr.val){
lastSorted = lastSorted.next;
}else{
ListNode prev = dummyHead;
while (prev.next.val <= curr.val){
prev = prev.next;
}
lastSorted.next = curr.next;
curr.next = prev.next;
prev.next = curr;
}
curr = lastSorted.next;
}
return dummyHead.next;
}
判断一棵树
给定一棵二叉树,其中已经没有重复值的节点,请判断该二叉树是否为搜索二叉树和完全二叉树。
public boolean[] judgeIt (TreeNode root) {
// write code here
return new boolean[]{
isBST(root,null,null),isCBT(root)};
}
/*
搜索二叉树判断
*/
public boolean isBST(TreeNode root, TreeNode min, TreeNode max){
if(root == null){
return true;
}
if(min != null && min.val > root.val){
return false;
}
if(max != null && max.val < root.val){
return false;
}
return isBST(root.left,min,root) && isBST(root.right,root,max);
}
/*
完全二叉树判断
*/
public boolean isCBT(TreeNode root){
if(root == null){
return true;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean flag = false;
while(!queue.isEmpty()){
TreeNode node = queue.poll();
if(node == null){
flag = true;
continue;
}
if(flag){
return false;
}
queue.offer(node.left);
queue.offer(node.right);
}
return true;
}
两个链表相加
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
public static ListNode addInList (ListNode head1, ListNode head2) {
if(head1 == null && head2 == null){
return null;
}else if(head1 == null){
return head2;
}else if(head2 == null){
return head1;
}
head1 = resver(head1);
head2 = resver(head2);
ListNode head = new ListNode(-1);
ListNode cur = head;
int flag = 0;
while (head1 != null || head2 != null){
int x = 0, y = 0;
if (head1 != null){
x = head1.val;
head1 = head1.next;
}
if (head2 != null){
y = head2.val;
head2 = head2.next;
}
int num = x + y + flag;
if (num / 10 != 0){
flag = 1;
}else{
flag = 0;
}
cur.next = new ListNode(num % 10);
cur = cur.next;
}
if (flag == 1){
cur.next = new ListNode(flag);
}
head = resver(head.next);
return head;
}
/*
反转链表
*/
public static ListNode resver(ListNode h){
ListNode pre = null, cur = h;
while (cur != null){
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
/*
输出链表
*/
public static void printNode(ListNode h){
while (h != null){
System.out.print(h.val + " ");
h = h.next;
}
System.out.println();
}
岛屿的周长
给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。
网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。
/*
直接遍历二维数组,当前元素为1时,结果加4,判断该元素的上方和左方有无相邻元素,若有则减去相邻的两边,即减2,最后返回结果即可
*/
public int islandPerimeter(int[][] grid) {
if(grid == null){
return 0;
}
int res = 0;
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(grid[i][j] == 1){
res += 4;
if(i > 0 && grid[i-1][j] == 1){
res -= 2;
}
if(j > 0 && grid[i][j-1] == 1){
res -= 2;
}
}
}
}
return res;
}
}
寻找第K大
有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。
给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数,保证答案存在。
public int findKth(int[] a, int n, int K) {
// write code here
return find(a,0,n-1,K);
}
public int find(int []a, int begin, int end, int K) {
int p = partition(a,begin,end);
if (p+1 < K) {
return find(a,p+1,end,K);
}
if (p+1 > K) {
return find(a,begin,p-1,K);
}
return a[p];
}
private int partition(int[] a, int begin, int end) {
int i = begin;
int j = end + 1;
int t = a[begin];
while (true) {
while(a[++i] > t) {
if (i >= end) break;
}
while(a[--j] < t) {
if (j <= begin) break;
}
if (i >= j) break;
exchange(a,i,j);
}
exchange(a,begin,j);
return j;
}
private void exchange(int[] a, int i, int j) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
最长的括号字串
给出一个仅包含字符’(‘和’)'的字符串,计算最长的格式正确的括号子串的长度。
对于字符串"(()“来说,最长的格式正确的子串是”()",长度为2.
再举一个例子:对于字符串")()())",来说,最长的格式正确的子串是"()()",长度为4.
public static int longestValidParentheses (String s) {
// write code here
Stack<Integer> stack = new Stack<>(); // 设定栈,存储左括号
stack.push(-1); // 压入-1,处理边界问题
int res = 0; // 结果存储变量
for (int i = 0;i < s.length();i++) {
// 如果是左括号则直接入栈
if (s.charAt(i) == '(') {
stack.push(i);
}else {
// 如果是右括号,则弹栈
stack.pop();
// 判空,若栈为空,则说明i左侧已经没有可用的左括号,此时将i压入栈中,防止空栈异常
if (stack.isEmpty()) {
stack.push(i);
}else {
// 长度计算时无需加1,因为预先弹栈,相当于已经加过1,且在01边界上因为初始化时压入-1进栈,因此也得以解决
res = Math.max(res, i - stack.peek());
}
}
}
return res;
}
设计getMin功能的栈
实现一个特殊功能的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作
/*
用两个 ArrayList 来辅助
*/
public int[] getMinStack (int[][] op) {
ArrayList<Integer> list = new ArrayList<>();
ArrayList<Integer> res = new ArrayList<>();
for (int i = 0; i < op.length; i++){
if (op[i][0] == 1){
list.add(op[i][1]);
}else if (op[i][0] == 2){
list.remove(list.size()-1);
}else if(op[i][0] == 3){
res.add(getMin(list));
}
}
int []num = new int[res.size()];
for (int i = 0; i < num.length; i++){
num[i] = res.get(i);
}
return num;
}
public int getMin(ArrayList<Integer> list){
int min = Integer.MAX_VALUE;
for(int val : list){
if (min > val){
min = val;
}
}
return min;
}
大数相加
以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回。
(字符串长度不大于100000,保证字符串仅由’0’~'9’这10种字符组成)
public static String solve (String s, String t) {
// write code here
if(s == null && t == null){
return null;
}else if(s == null || t == null){
return s != null ? s : t;
}
String res = "";
int flag = 0;
int lens = s.length() - 1, lent= t.length() - 1;
while(lens>= 0 || lent >= 0){
int x = 0, y = 0;
if(lens >= 0){
x = s.charAt(lens--) - '0';
}
if(lent >= 0){
y = t.charAt(lent--) - '0';
}
int num = x + y + flag;
flag = num / 10;
res = num % 10 + res;
}
if (flag != 0){
res = flag + res;
}
return res;
}
合并K个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
方法一:
public static ListNode mergeKLists(ListNode[] lists) {
int n = lists.length ;
if(n == 0) {
return null;
}else if(n == 1) {
return lists[0];
}
while(n > 1) {
int k = 0;
for(int i = 0 ;i < n ;i+=2) {
if(i == n - 1) {
lists[k++] = lists[i];
}else {
lists[k++] = mergeTwoLists(lists[i], lists[i + 1]);
}
}
n = k;
}
return lists[0];
}
public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) {
return l2;
}else if(l2 == null) {
return l1;
}else {
int n1 = listSize(l1),n2 = listSize(l2);
int []arr = new int[n1+n2];
int i = 0;
while(i < n1 && l1 != null) {
arr[i++] = l1.val;
l1 = l1.next;
}
while(i < n1 + n2 && l2 != null) {
arr[i++] = l2.val;
l2 = l2.next;
}
Arrays.sort(arr);
ListNode res = new ListNode();
ListNode head = res;
for(int k = 0 ; k < n1 + n2 ; k++) {
ListNode newnode = new ListNode(arr[k]);
head.next = newnode;
head = head.next;
}
return res.next;
}
}
/*
public static void print(ListNode head) {
while(head != null) {
System.out.print(head.val+" ");
head = head.next;
}
System.out.println();
}
*/
public static int listSize(ListNode head) {
int i = 0;
while(head != null) {
head = head.next;
i++;
}
return i;
}
方法二:
public static ListNode mergeKLists(ArrayList<ListNode> lists) {
if(lists == null || lists.size() < 1){
return null;
}
int n = lists.size();
if (n == 1){
return lists.get(0);
}
if(n % 2 == 1){
lists.add(null);
}
ArrayList<ListNode> cur = new ArrayList<>();
for (int i = 0; i < lists.size(); i += 2){
cur.add(merge(lists.get(i), lists.get(i+1)));
}
return mergeKLists(cur);
}
/*
合并两个链表
*/
public static ListNode merge(ListNode l1, ListNode l2){
if (l1 == null && l2 == null){
return null;
}else if (l1 == null || l2 == null){
return l1 != null ? l1 : l2;
}
ListNode head = new ListNode(-1);
ListNode cur = head;
while (l1 != null && l2 != null){
if (l1.val < l2.val){
cur.next = l1;
l1 = l1.next;
}else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
if (l1 != null){
cur.next = l1;
}else {
cur.next = l2;
}
return head.next;
}
链表的奇偶重排
给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。
注意是节点的编号而非节点的数值。
public ListNode oddEvenList (ListNode head) {
// write code here
// even偶数,odd奇数
ListNode even = new ListNode(-1), odd = new ListNode(-1);
ListNode heven = even, hodd = odd;
while(head != null && head.next != null){
hodd.next = head;
hodd = hodd.next;
heven.next = head.next;
heven = heven.next;
head = heven.next;
}
// 当前不为空,则一定为奇数
if (head != null){
hodd.next = head;
hodd = hodd.next;
}
heven.next = null;
hodd.next = even.next;
return odd.next;
}
数组未出现的最小正整数
给定一个无序数组arr,找到数组中未出现的最小正整数
例如arr = [-1, 2, 3, 4]。返回1
arr = [1, 2, 3, 4]。返回5
[要求]
时间复杂度为O(n)O(n),空间复杂度为O(1)O(1)
public static int minNumberdisappered (int[] arr) {
// write code here
Arrays.sort(arr);
int k = 1;
for(int val: arr){
if(val > 0){
if(val == k){
k++;
}else{
return k;
}
}
}
return k;
}
数组单调和
现定义数组单调和为所有元素i的f(i)值之和。这里的f(i)函数定义为元素i左边(不包括其自身)小于等于它的数字之和。请设计一个高效算法,计算数组的单调和。
给定一个数组A同时给定数组的大小n,请返回数组的单调和。保证数组大小小于等于500,同时保证单调和不会超过int范围。
测试样例:
[1,3,5,2,4,6],6
返回:27
public int calcMonoSum(int[] A, int n) {
int count = 0;
for(int i = 1 ; i < n ;i++){
for(int j = i-1 ; j >= 0 ; j--){
if(A[j]<=A[i]){
count += A[j];
}
}
}
return count;
}
栈和排序
给你一个1->n的排列和一个栈,入栈顺序给定
你要在不打乱入栈顺序的情况下,对数组进行从大到小排序
当无法完全排序时,请输出字典序最大的出栈序列
示例1
[2,1,5,3,4]
返回值
[5,4,3,1,2]
public static int[] solve(int[] a) {
if (a == null || a.length == 0) {
return a;
}
int index = 0, n = a.length;
int Max = Integer.MIN_VALUE;
int[][] maxArr = new int[n][2];
for (int i = n - 1; i >= 0; i--) {
if (a[i] > Max) {
Max = a[i]; // 找出最大的数
index = i; // 对应下标
}
maxArr[i][0] = Max;
maxArr[i][1] = index;
}
Stack<Integer> stack = new Stack<>();
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
// 入栈
stack.push(a[i]);
if (maxArr[i][1] == i) {
// 当与最大值的下标一致时,把数放进list
list.add(stack.pop());
// 当栈内的数数比数组下一个数大时,出栈,放进list
while (!stack.isEmpty() && i < n - 1 && stack.peek() > maxArr[i + 1][0]) {
list.add(stack.pop());
}
}
}
while (!stack.isEmpty()) {
list.add(stack.pop());
}
int[] res = new int[a.length];
for (int i = 0; i < list.size(); i++) {
res[i] = list.get(i);
}
return res;
}
信封嵌套问题
信封嵌套问题
给n个信封的长度和宽度。如果信封A的长和宽都小于信封B,那么信封A可以放到信封B里,请求出信封最多可以嵌套多少层。
示例1
[[3,4],[2,3],[4,5],[1,3],[2,2],[3,6],[1,2],[3,2],[2,4]]
返回值
4
public int maxLetters (int[][] envelope) {
// write code here
// write code here
List<int[]> list = new ArrayList<>();
int n = envelope.length;
for(int []rows : envelope){
list.add(rows);
}
// 长度相同则按宽度,宽度的大在上,否则长度小的在上
Collections.sort(list, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0];
}
});
// 最长上升子序列问题
int []dp = new int[n];
Arrays.fill(dp,1);
int count = 1;
for (int i = 0; i < n; i++){
for (int j = 0; j < i; j++){
int []x = list.get(i);
int []y = list.get(j);
if (y[1] < x[1] && y[0] < x[0]){
dp[i] = Math.max(dp[i], dp[j]+1);
count = Math.max(dp[i], count);
}
}
}
return count;
}
二叉树根节点到叶子节点的所有路径和
给定一个仅包含数字 0−9 的二叉树,每一条从根节点到叶子节点的路径都可以用一个数字表示。
例如根节点到叶子节点的一条路径是1→2→3,那么这条路径就用 123 来代替。
找出根节点到叶子节点的所有路径表示的数字之和
根节点到叶子节点的路径 1→2 用数字 12 代替
根节点到叶子节点的路径 1→3 用数字 13 代替
所以答案为 12+13=25
public int sumNumbers (TreeNode root) {
// write code here
return helper(root,0);
}
public int helper(TreeNode root, int sum){
if(root == null){
return 0;
}
sum = sum * 10 + root.val;
if(root.left == null && root.right == null){
return sum;
}else{
return helper(root.left, sum) + helper(root.right, sum);
}
}
在两个长度相等的排序数组中找到上中位数
给定两个有序数组arr1和arr2,已知两个数组的长度都为N,求两个数组中所有数的上中位数。
上中位数:假设递增序列长度为n,若n为奇数,则上中位数为第n/2+1个数;否则为第n/2个数
[要求]
时间复杂度为O(logN),额外空间复杂度为O(1)
/*
public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) {
// write code here
int n = arr1.length;
int idx1 = 0;
int idx2 = 0;
while(n>1){
if(arr1[idx1] <= arr2[idx2]){
idx1++;
}else{
idx2++;
}
n--;
}
return Math.min(arr1[idx1],arr2[idx2]);
}
*/
public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) {
// write code here
int n = arr1.length;
int left = 0;
int right = n;
while(left<right){
int mid = left + ((right-left) >> 1);
int j = n - mid;
if(arr1[mid] < arr2[j-1]){
left = mid+1;
}else{
right = mid;
}
}
int num1 = left;
int num2 = n - left;
if(num1 == n){
return arr1[n-1];
}
if(num2 == n){
return arr2[n-1];
}
return Math.max(arr1[num1-1], arr2[num2-1]);
}
最长公共子串
给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。
示例1
输入
“1AB2345CD”,“12345EF”
返回值
“2345”
public String LCS (String str1, String str2) {
if(str1 == null || str2 == null){
return "-1";
}
char []c1 = str1.toCharArray();
char []c2 = str2.toCharArray();
int len1 = c1.length , len2 = c2.length;
int begin = 0 , max = 0 , end = 0;
for(int i = 0 ; i < len1 ; i++){
for(int j = 0 ; j < len2 ; j++){
int k = 0;
while(i + k < len1 && j + k < len2 && c1[i+k] == c2[j+k]){
k++;
if(k > max){
begin = i;
end = i + k;
max = k;
}
}
}
}
if(max == 0){
return "-1";
}
return str1.substring(begin , end);
}
二叉树中寻两个节点的最近公共祖先
给定一棵二叉树以及这棵树上的两个节点 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。
/*
关键还是找到最近公共节点的特征:
1. 如果该节点不是O1也不是O2,那么O1与O2必然分别在该节点的左子树和右子树中
2. 如果该节点就是O1或者O2,那么另一个节点在它的左子树或右子树中
稍微可以优化的一点就是,遇到O1或者O2节点就不往下递归了,把O1或者O2节点一层层往上传。
*/
public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
// write code here
if(root == null){
return -1;
}
if(root.val == o1 || root.val == o2){
return root.val;
}
int left = lowestCommonAncestor(root.left, o1, o2);
int right = lowestCommonAncestor(root.right, o1, o2);
if(left == -1){
return right;
}
if(right == -1){
return left;
}
return root.val;
}
回文数字
在不使用额外的内存空间的条件下判断一个整数是否是回文数字
提示:
负整数可以是回文吗?(比如-1)
如果你在考虑将数字转化为字符串的话,请注意一下不能使用额外空间的限制
你可以将整数翻转。但是,如果你做过题目“反转数字”,你会知道将整数翻转可能会出现溢出的情况,你怎么处理这个问题?
/*
每次将数字的第一位和最后一位进行对比。
如果不相等直接返回false,相等则将数字的第一位和最后一位去掉,重复以上流程。
*/
public static boolean isPalindrome (int x) {
// write code here
if(x < 0){
return false;
}
int div = 1;
while (x / div >= 10){
div *= 10;
}
System.out.println(div);
while (x > 0){
int l = x / div;
int r = x % 10;
if (l != r){
return false;
}
x = x % div / 10;
div /= 100;
}
return true;
}
换钱的最少货币
题目描述
给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。
如果无解,请返回-1.
【要求】
时间复杂度O(n×aim),空间复杂度On。
/*
// 超时了
private static int count = Integer.MAX_VALUE;
public static int minMoney (int[] arr, int aim) {
// write code here
Arrays.sort(arr);
helper(new ArrayList<Integer>(), aim, arr);
return count;
}
public static void helper(ArrayList<Integer> list, int val, int []arr){
if (val == 0){
count = Math.min(count, list.size());
return;
}
for (int i = arr.length - 1; i >= 0; i--){
int cur = val - arr[i];
if (cur >= 0){
list.add(arr[i]);
helper(list, cur, arr);
list.remove(list.size()-1);
}
}
}
*/
/*
动态规划
*/
public static int minMoney (int[] arr, int aim) {
// write code here
if(arr == null || arr.length == 0)
return -1;
Arrays.sort(arr);
// dp[i] 换得金额i能用的最少硬币数
int[] dp = new int[aim + 1];
dp[0] = 0;
for(int i = 1; i < aim + 1; ++ i){
int min = Integer.MAX_VALUE;
for(int j = 0; j < arr.length; ++ j){
int val = i - arr[j];
if(val < 0)
break;
else{
if(dp[val] == Integer.MAX_VALUE)
continue;
else
min = Math.min(min, dp[val] + 1);
}
}
dp[i] = min;
}
return dp[aim] == Integer.MAX_VALUE ? -1 : dp[aim];
}