2019.06.06 - 第一天
【1.】1描述 : 给出两个整数 a 和 b , 求他们的和。
class Solution {
/*
* param a: The first integer
* param b: The second integer
* return: The sum of a and b
*/
public int aplusb(int a, int b) {
// 主要利用异或运算来完成
// 异或运算有一个别名叫做:不进位加法
// 那么a ^ b就是a和b相加之后,该进位的地方不进位的结果
// 然后下面考虑哪些地方要进位,自然是a和b里都是1的地方
// a & b就是a和b里都是1的那些位置,a & b << 1 就是进位
// 之后的结果。所以:a + b = (a ^ b) + (a & b << 1)
// 令a' = a ^ b, b' = (a & b) << 1
// 可以知道,这个过程是在模拟加法的运算过程,进位不可能
// 一直持续,所以b最终会变为0。因此重复做上述操作就可以
// 求得a + b的值。
while (b != 0) {
int _a = a ^ b;
int _b = (a & b) << 1;
a = _a;
b = _b;
}
return a;
}
};
【2】2描述:设计一个算法,计算出n阶乘中尾部零的个数
https://blog.youkuaiyun.com/nwsuaf_uestc/article/details/78788932
点睛之笔:我们发现只有一个数是五的倍数的时候,阶乘的后边就是0,如果一个数可以分解成5*5或者5*10、5*15那么就会结尾添加两个0,也就是说一个数除以五如果大于5那么就会多添加一个0。
public class Solution {
/*
* @param n: An integer
* @return: An integer, denote the number of trailing zeros in n!
*/
public long trailingZeros(long n) {
// write your code here, try to do it without arithmetic operators.
long sum = 0;
int count = 0;
while(n >= 5){
n = n / 5;
sum = sum + n;
count++;
}
return sum;
}
}
2019.06.08 - 第二天
【3】3统计数字
结论如下:我们可以得到以下规律:
- 当某一位的数字小于i时,那么该位出现i的次数为:更高位数字x当前位数
- 当某一位的数字等于i时,那么该位出现i的次数为:更高位数字x当前位数+低位数字+1
- 当某一位的数字大于i时,那么该位出现i的次数为:(更高位数字+1)x当前位数
class Solution { /* * param k : As description. * param n : As description. * return: An integer denote the count of digit k in 1..n */ public int digitCounts(int k, int n) { // write your code here int cnt = 0; for (int i = k; i <= n; i++) { cnt += singleCount(i, k); } return cnt; } public int singleCount(int i, int k) { if (i == 0 && k == 0) return 1; int cnt = 0; while (i > 0) { if (i % 10 == k) { cnt++; } i = i / 10; } return cnt; } };
【4】*************5在数组中找到第 k 大的元素。
public class Kth {
/*
* @param k : description of k
* @param nums : array of nums
* @return: description of return
*/
public static int kthLargestElement(int k, int[] nums) {
if (nums == null || nums.length == 0 || k < 1 || k > nums.length){
return -1;
}
return partition(nums, 0, nums.length - 1, nums.length - k);
}
private static int partition(int[] nums, int start, int end, int k) {
if (start >= end) {
return nums[k];
}
int left = start, right = end;
int pivot = nums[(start + end) / 2];
while (left <= right) {
while (left <= right && nums[left] < pivot) {
left++;
}
while (left <= right && nums[right] > pivot) {
right--;
}
if (left <= right) {
swap(nums, left, right);
left++;
right--;
}
}
if (k <= right) {
return partition(nums, start, right, k);
}
if (k >= left) {
return partition(nums, left, end, k);
}
return nums[k];
}
private static void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
public static void main(String[] args) {
int k = 1;
int[] nums = {5,3,7,2,1};
int res = kthLargestElement(k, nums);
System.out.println(res);
}
}
【5】6合并两个有序升序的整数数组A和B变成一个新的数组。新数组也要有序。
public class MergeArray {
public static void main(String[] args) {
int[] A = {1,2,3,4};
int[] B = {2,4,6,8,9,15,34};
int[] res = merge(A,B);
for (int re : res) {
System.out.print(re + ",");
}
}
public static int[] merge(int[] a, int[] b){
int i = 0, j = 0, index = 0;
int[] C = new int[a.length + b.length];
// 这里判断数组都没有完毕的情况
while (i < a.length && j < b.length){
// 循环赋值
if (a[i] < b[j]){
C[index++] = a[i++];
}else {
C[index++] = b[j++];
}
}
//当 a 数组长度没有完结时
while (i < a.length){
C[index++] = a[i++];
}
//当 b 数组长度没有完结时
while (j < b.length){
C[index++] = b[j++];
}
return C;
}
}
【6】7二叉树的序列化与反序列化
【分析1】首先明确序列化与反序列化二叉树。所谓的序列化二叉树就是按照树的某种遍历方式遍历二叉树,将每个结点的值以某种特殊符号(例如,)分隔保存到一个字符串中,如果结点为空,则使用另外一种特殊符号(例如#)表示。反序列化的意思就是根据序列化的字符串重构出该二叉树。这里需要特别说明一点的是,遍历到叶子结点后,还需要继续向下遍历,结点为空,则用特殊字符(例如#)表示。
【分析2】对于二叉树的遍历方式选择上,原则上前、中、后和层次遍历都是可以的。但是为了能够很容易的获取二叉树的根结点,因此我们可以选择前序遍历顺序,这样遍历出来的第一个元素就是二叉树的根结点对应的值。
【分析3】对于二叉树的序列化来说,现在就变成了一个前序遍历二叉树的过程。如果当前结点不为空,则将当前结点对应的值加入到字符串中;如果当前结点为空,则将特殊字符(例如,#)加入到字符串中。
【分析4】根据前序序列化的二叉树字符串,我们需要重构该二叉树。根据前序遍历的特点,我们在获取根结点后,首先应该一直向左构建左子树,直到遇到空结点。如果当前结点为空,则应该返回到它的父结点,然后构造它的父结点的右子树。以此类推,显然这也是一个递归进行的过程。
class Solution {
/**
* This method will be invoked first, you should design your own algorithm
* to serialize a binary tree which denote by a root node to a string which
* can be easily deserialized by your own "deserialize" method later.
*/
public String serialize(TreeNode root) {
if (root == null) {
return "{}";
}
// 初始化树
ArrayList<TreeNode> queue = new ArrayList<TreeNode>();
// 将根节点入队列
queue.add(root);
// 递归将树节点入队列
for (int i = 0; i < queue.size(); i++) {
TreeNode node = queue.get(i);
if (node == null) {
continue;
}
queue.add(node.left);
queue.add(node.right);
}
// 这一步删除操作有点蒙蔽
while (queue.get(queue.size() - 1) == null) {
queue.remove(queue.size() - 1);
}
// 将队列元素输出到字符串
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append(queue.get(0).val);
for (int i = 1; i < queue.size(); i++) {
if (queue.get(i) == null) {
sb.append(",#");
} else {
sb.append(",");
sb.append(queue.get(i).val);
}
}
sb.append("}");
return sb.toString();
}
/**
* This method will be invoked second, the argument data is what exactly
* you serialized at method "serialize", that means the data is not given by
* system, it's given by your own serialize method. So the format of data is
* designed by yourself, and deserialize it here as you serialize it in
* "serialize" method.
*/
public TreeNode deserialize(String data) {
if (data.equals("{}")) {
return null;
}
// 去除{}, 逗号分割数据节点
String[] vals = data.substring(1, data.length() - 1).split(",");
// 建立树链
ArrayList<TreeNode> queue = new ArrayList<TreeNode>();
// 根节点
TreeNode root = new TreeNode(Integer.parseInt(vals[0]));
queue.add(root);
// 开始遍历
int index = 0;
boolean isLeftChild = true;
for (int i = 1; i < vals.length; i++) {
// 不管空节点,前序遍历 入队列
if (!vals[i].equals("#")) {
TreeNode node = new TreeNode(Integer.parseInt(vals[i]));
if (isLeftChild) {
queue.get(index).left = node;
} else {
queue.get(index).right = node;
}
queue.add(node);
}
if (!isLeftChild) {
index++;
}
isLeftChild = !isLeftChild;
}
return root;
}
}
【7】8字符串旋转问题
输入: str="abcdefg", offset = 3
输出: str = "efgabcd"
样例解释: 注意是原地旋转,即str旋转后为"efgabcd"
class Solution:
def rotateString(self, s, offset):
s1 = s[:-offset]
s2 = s[-offset:]
# 直接s = s2 + s1 这样是通不过的
temp = s2 + s1
for i in range(len(temp)):
s[i] = temp[i]
# class Solution:
# def rotateString(self, s, offset):
# # write you code here
# if len(s) > 0:
# offset = offset % len(s)
# temp = (s + s)[len(s) - offset : 2 * len(s) - offset]
# for i in range(len(temp)):
# s[i] = temp[i]
【8】12. 带最小值操作的栈
使用两个仅支持 pop
和 push
的栈就可以完成, stack
储存压入的数据, minStack
储存最小值.
push
直接把元素压入 stack, 对于 minStack, 如果它为空则直接压入, 反之压入当前元素与 minStack 栈顶的最小值pop
两个栈都弹出一个元素, 返回 stack 弹出的元素min
返回 minStack 的栈顶
还可以令 minStack 为单调栈, 即
push
时只有元素更小的时候才放入这个栈, 而pop
时只有栈顶与stack栈顶相同时才弹出这样可以节约一定的空间, 但是实质上空间复杂度仍然是 O(n), 且多了一些判断, 并不一定更优
public class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minStack;
public MinStack() {
stack = new Stack<Integer>();
minStack = new Stack<Integer>();
}
public void push(int number) {
stack.push(number);
if (minStack.isEmpty()) {
minStack.push(number);
} else {
minStack.push(Math.min(number, minStack.peek()));
}
}
public int pop() {
minStack.pop();
return stack.pop();
}
public int min() {
return minStack.peek();
}
}