5分钟手写快排归并广度-二叉树栈前序中序后序
面试的痛点在于不知道掌握什么--但是目前看来-抓住中小公司的共同点--然后海投
有的会本地IDE-运行-最好提前准备一个Solution的class用于写
快排
public class Solution {
public static void main(String[] args) {
int[] arr = new int[] {9,4,6,8,3,10,4,6};
quickSort(arr,0,arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int left, int right) {
if (left < right) {
int pivot = partition(arr, left, right);
quickSort(arr, left, pivot - 1);
quickSort(arr, pivot + 1, right);
}
}
private static int partition(int[] arr, int left, int right) {
int pivot = left;
int index = pivot +1 ;
for (int i = index; i < right; i++) {
if (arr[i] < arr[pivot]) {
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
}
int temp = arr[pivot];
arr[pivot] = a[index-1];
arr[index-1] = temp;
return index-1;
}
}
还有归并之后写
桶排
值<=t和下标之差<=k
class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
int n = nums.length;
//桶的大小为t+1,允许最大元素和最小元素之差为t
long w = (long) t + 1;
//因为一个桶有两个元素就会返回true,因此一个桶只有一个元素,可以用哈希表的一条key-value表示桶
Map<Long, Long> map = new HashMap<Long, Long>();
for (int i = 0; i < n; i++) {
long id = getID(nums[i], w);
//桶里已有元素x,nums[i]和x同属一个桶,值符合范围
//只保留下标 i 之前的 k 个元素,因此下标也符合范围
//桶有两个元素就会返回,因此一个桶只有一个元素
if (map.containsKey(id)) {
return true;
}
//前一个桶有一个元素,并且值的范围符合要求
if (map.containsKey(id - 1) && Math.abs(nums[i] - map.get(id - 1)) < w) {
return true;
}
//后一个桶有一个元素,并且值的范围符合要求
if (map.containsKey(id + 1) && Math.abs(nums[i] - map.get(id + 1)) < w) {
return true;
}
//没有和nums[i]匹配的元素,把nums[i]加入自己的桶里
map.put(id, (long) nums[i]);
//下标范围[i-k+1, i],从nums[i-k]所在桶移除元素
if (i >= k) {
map.remove(getID(nums[i - k], w));
}
}
return false;
}
public long getID(long x, long w) {
//非负数区间,如[0, t] 会被归到 id=0
//其余的区间,如[(n-1)t+1, nt+1],每t+1个元素会被归到 id = n-1
if (x >= 0) {
return x / w;
}
//负数区间,如[-t, -1] 会被归到 id=-1
//其余的区间,如[-(n+1)t-1, -nt-1],每t+1个元素会被归到 id = -(n+1)
return (x + 1) / w - 1;
}
}
- getId: 0-w-1--->0, -1 to -w -------> -1, 所以才会有((x+1)/w ) -1
- 溢出所以使用long
- 每个桶内部元素只会有一个,因为第一次判断如果已经存在就说明现在有两个元素满足了,就可以直接返回true
- 然后如果自己的桶不存在,就看相邻的桶的元素是不是小于W的,注意相邻的也只会存在一个,因为两个就会返回true
- 然后都没有就放进桶,记住还要删除i-k的,因为在下次遍历的时候我们只需要查看前k个元素的即可,所以只需要维护区间K
- 记住下次遍历是i+1,所以这里删除i-k,这样就是i-k+1 to i - k + k,
前序遍历
//核心
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
list.add(node.val);
if(node.right!=null){
stack.push(node.right);
}
if(node.left!=null){
stack.push(node.left);
}
}
return list.stream().mapToInt(Integer::intValue).toArray();
这个不刷就不好快速写出来,实际上就是先读取根节点,再插入右节点,再插入左节点
出栈-读取-插入右节点-插入左节点-循环
这样子就是先读取根,随后读取左,右节点就一直压着
中序遍历
while(root!=null||!stack.isEmpty()){
while(root!=null){
stack.push(root);
root = root.left;
}
TreeNode node = stack.pop();
list.add(node.val);
root = node.right;
}
return list.stream().mapToInt(Integer::intValue).toArray();
我怀疑手撕算法一般真的出前序不出中序,因为中序有点绕;
首先我们需要找到最左边的节点,因此必须一开始就有循环压栈;
那么随后弹出读取-这个相当于根节点了-之后就必须读取右节点;
root等于当前节点右节点-继续迭代
那么如果为空,也没关系,只要栈不为空即可;
所以就是--循环压栈root-最左节点-赋值root最左叶节点右孩子-迭代循环
后序遍历
TreeNode pre = null;
while(root!=null||!stack.isEmpty()){
while(root!=null){
stack.push(root);
root = root.left;
}
TreeNode node = stack.pop();
if(node.right==null||node.right==pre){
list.add(node.val);
pre = node;
}else{
stack.push(node);
root = root.right;
}
}
这个貌似pre就很难理解-中序需要root来帮助遍历;
后续也是遍历到最左-假设此时右边有节点-就会重新把node压入栈中
然后把node.right压入栈中--也就是找到最左节点的右节点进行递归
而终止条件就是右节点为空或者遍历过了。那么假设右节点遍历过了
就应该使用pre指定-节点。
右视图-层序遍历
que.offer(root);
TreeNode curr = null;
while(!que.isEmpty()){
int size = que.size();
for(int i = 0;i<size;i++){
curr = que.poll();
if(curr.left!=null) que.offer(curr.left);
if(curr.right!=null) que.offer(curr.right);
}
ans.add(curr.val);
}
return ans;
如果不是求每一层的最右边一个,只是遍历的列表,可以不使用size

查看1道真题和解析