算法笔记4~5章

第四章

哈希表

哈希表的简单介绍
1)哈希表在使用层面上可以理解为一种集合结构
2)如果只有key,没有伴随数据value,可以使用HashSet结构(C++中叫UnOrderedSet)
3)如果既有key,又有伴随数据value,可以使用HashMap结构(C++中叫UnOrderedMap)
4)有无伴随数据,是HashMap和HashSet唯一的区别,底层的实际结构是一回事
5)使用哈希表增(put)、删(remove)、改(put)和查(get)的操作,可以认为时间复杂度为O(1),但是常数时间比较大
6)放入哈希表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
7)放入哈希表的东西,如果不是基础类型,内部按引用传递,内存占用是这个东西内存地址的大小有关哈希表的原理,
将在提升班“与哈希函数有关的数据结构”一章中讲叙原理

有序表

1)有序表在使用层面上可以理解为一种集合结构
2)如果只有key,没有伴随数据value,可以使用TreeSet结构(C++中叫OrderedSet)
3)如果既有key,又有伴随数据value,可以使用TreeMap结构(C++中叫OrderedMap)
4)有无伴随数据,是TreeSet和TreeMap唯一的区别,底层的实际结构是一回事
5)有序表和哈希表的区别是,有序表把key按照顺序组织起来,而哈希表完全不组织
5)红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同
6)放入有序表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
7)放入有序表的东西,如果不是基础类型,必须提供比较器,内部按引用传递,内存占用是这个东西内存地址的大小
8)不管是什么底层具体实现,只要是有序表,都有以下固定的基本功能和固定的时间复杂度

有序表的固定操作

  1. void put(K key, V value):将一个(key, val ue)记录加入到表中,或者将key的记录更新成 value.
  2. V get(K key):根据给定的key,查询 value并返回。
  3. void remove(K key):移除key的记录
  4. boolean containskey(K key):询问是否有关于key的记录。
  5. K firstkey ():返回所有键值的排序结果中,最左(最小)的那个。
  6. K laskey ():返回所有键值的排序结果中,最右(最大)的那个。
  7. K floorKey(K key):如果表中存入过key,返回key;否则返回所有键值的排序结果中,key的前一个
  8. K ceilingKey(K key):如果表中存入过key,返回key;否则返回所有键值的排序结果中,
    key的后一个。
    以上所有操作时间复杂度都是0(logN),N为有序表含有的记录数
    有关有序表的原理,将在提升班“有序表详解”一章中讲叙原理

注: 有序表和哈希表在,大量刷题过程中,不需要知道原理。需要知道原理的题的都是难题,中等难度都不会涉及到内部原理。

链表

换头返回node,不换头返回void
链表方法论

题目1
反转单链表,双链表。

[分析]:用一个cur节点分析,想办法调整next 或 pre 指向。有点像交换值,记得让cur后移直至末尾。

题目2
Leetcode.234 是否是回文链表。
笔试:
1)栈
2)快慢指针(把握边界, leetcode题解有注释),找中点,只把右边放入栈中注意比较即可。

面试:
1)快慢指针找中心
2)反转右半部分,和左边逐一比较
3)恢复成原链表

题目3
给定key,链表小于在左,等于在中,大于在右。
笔试:
[分析] 用数组储存,partition一下,再复原回去原链表

面试:
在这里插入图片描述
[分析] 定义六个指针。注意如果没有小于部分,大于部分,等于部分时的讨论。

题目4
在这里插入图片描述

笔试:
[分析]:用hashmap存储原节点其克隆节点。把克隆节点串起来就行了;
copyList

面试:
[分析]:
1)先把克隆节点都连在原节点后面串起来
2)把克隆节点rand指针指向其原节点rand指针的下一个
3)分离链表
额外空间O(1)
题目5 hard
链表相交问题
证明单链表有环
1)哈希set
2)快慢指针
      1.快2step, 慢1step. 指针相遇后,
      2.快指针回到原点,然后每人都1步1步挪,相遇就是第一次入环节点。(有环返回入环loop节点,无环返回空)
判断是否有环
由于单链表的结构,两个单链表相交必定兵合一处,"Y"字形状
由于单链表的结构,要么两个都有环,要么都没环,不可能是一个有环一个无环
1)都无环
     让长的那一部分,截到短一样长,一起走,相等了就是相遇节点, 返回相遇节点loop
两个单链表是否相交
相交的第一个节点
     让6的部分和4对称,一起向下走,相等就相交

public static Node noLoop(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        Node cur1 = head1;
        Node cur2 = head2;
        int n = 0;	//只用一个变量记录,两链表长度之差
        while (cur1.next != null) {
            n++;
            cur1 = cur1.next;
        }
        while (cur2.next != null) {
            n--;
            cur2 = cur2.next;
        }
        if (cur1 != cur2) {
            return null;
        }
        //n: 链表1长度减去链表2长度的值
        cur1 = n > 0 ? head1 : head2; //谁长谁的头变cur1
        cur2 = cur1 == head1 ? head2 : head1; //谁短谁的头变cur2
        n = Math.abs(n);
        while (n != 0) {	//先走到一样长
            n--;
            cur1 = cur1.next;
        }
        while (cur1 != cur2) {
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }

2)都有环

有环的情况
1) 不相交,让loop1往下走直到回到loop1(转一圈), 遇到 loop2就是<3>,否则就是<1>。
2) loop1==loop2,让loop1当作无环情况中的end尾节点进行上述操作就可。
3) 返回loop2或者loop1

public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
        Node cur1 = null;
        Node cur2 = null;
        if (loop1 == loop2) {	//case2, 参考上述无环代码
            cur1 = head1;
            cur2 = head2;
            int n = 0;
            while (cur1 != loop1) {
                n++;
                cur1 = cur1.next;
            }
            while (cur2 != loop2) {
                n--;
                cur2 = cur2.next;
            }
            cur1 = n > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            n = Math.abs(n);
            while (n != 0) {
                n--;
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } 
        else {//情况1,3
            cur1 = loop1.next; //case3
            while (cur1 != loop1) {// 转一圈回来
                if (cur1 == loop2) {// 看环里有没有loop2,有就是case3,
                    return loop1;
                }
                cur1 = cur1.next;
            }
            return null;// case1
        }
    }

第五章

递归序

递归序
1) 第一次函数体 , -->>先序遍历:
2) 第二次回到函数体 , -->>中序遍历:
3) 第三次回到函数体 , ->>后序遍历:

非递归深度遍历(栈)

先序遍历

打印顺序 : 头 -> 左 -> 右

图解 :
先序遍历
先弹栈,先右入后左入,(则弹出为 先左后右==与原本顺序一致)
先序遍历-code
后序遍历

需要辅助栈,为的是逆序放到辅助栈里去
注: 入栈左右,出栈右左
后序遍历
后序--code

中序遍历
中序遍历图解
左边一次性全入,然后处理cur节点,再把右树 重复操作
中序技巧
就是先左再头,然后右树上先左再头
中序code

广度优先遍历(队列)

队列,先弹出,放左再放右
广度优先遍历

题目1
求一个二叉树的最大宽度

  1. HashMap
    二叉树的最大宽度

最后一个走完,队空,因此退出循环,但是没有进行if else里的max ,curLevel的更新。
while外补上max = Math.max(max, curLevelNodes)

2.不用哈希表
NodeCurEnd, NodeNextEnd, CurLevel,max

class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
      
        TreeNode curLevelEnd = root;//当前层的最后一个
        TreeNode nextLevelEnd = null;//下一层的最后一个
        int curLevelNodes = 0; 
        int max = Integer.MIN_VALUE;//返回值
    
        while( !queue.isEmpty() ){
            root = queue.poll();//pop
            System.out.print("\t" + root.val);

            if(root.left!= null){//一直更新下一层的值
                queue.add(root.left);
                nextLevelEnd = root.left;
            }
            if(root.right!= null){
                queue.add(root.right);
                nextLevelEnd = root.right;
            }

            if(root != curLevelEnd){//不到当前最后一个节点
                curLevelNodes++;//当前层的节点数++
            }
            else{
                curLevelEnd = nextLevelEnd;//进入下一层,末尾节点赋值
                nextLevelEnd = null;//更新下一层的末尾节点,同时更新max
                max = max > curLevelNodes ? max : curLevelNodes+1;
                curLevelNodes = 0;//进入下一层后,此层节点数reset
            }

        }

        return max;
    }
}

特殊二叉树

搜索二叉树 BST
中序遍历,升序(没看懂)
isBST
isBST

完全二叉树
完全二叉树
遇到第一个带圈的节点后,后面的节点都得是叶子节点。

满二叉树
最大深度d和节点个数n满足 n=2^d-1

平衡二叉树(总结二叉树的套路)
(左右子树高度差不超过1)
平衡二叉树
套路: 左树要信息(bool isBalanced,int hight),右树要信息,3个条件取交集。

套路

套路(解决一切树形DP,动态规划(二叉树面试最难的))
用于: 问左树要信息,右树要信息把问题解了
求树里的中位数就无法此套路解
面试考核的题,旨在优化,所以这很好用。
1》 BST
(左数 BST
&& 右数 BST
&& 左max < cur.val < 右min)
!!!注意:递归套路,返回的东西应该是一样的,因此左右数都要返回三个信息(bool isBST,int max ,int min)
关键代码

2》 满二叉树
(int height, int nodes)
nodes==(1<<height)-1 //- 的优先级比<<高

题目1
o1, o2最低公共祖先
1》 HashMap(self, father)
先把所有节点录入HashMap
录入节点
然后用一个HashSet存储 o1的父节点路径,至到根节点
用o2往祖先索引,直至有和 o1的父节点路径(HashSet) 重合的节点
2》 分析:
1。 o1直接就是o2的祖先,或者o2直接就是o1的祖先。
2。o1和o2,往回有交汇

public static Node lowestAncestor(Node head, Node o1, Node o2) {
		if (head == null || head == o1 || head == o2) {
			return head;
		}
		Node left = lowestAncestor(head.left, o1, o2);//左下
		Node right = lowestAncestor(head.right, o1, o2);//右下
		//左,右都有返回值,即2。返回当前的
		if (left != null && right != null) {
			return head;
		}
		return left != null ? left : right;//左右两数不都有返回值,返回不空的
	}

题目2 后继节点
比普通二叉树多了一个father属性,head的father是空
中序遍历结果的后一个,最后一个节点后继为null
中序遍历,用于参考观察
有右树,=》找到右数最左下的那个
无右数,=》往回,至到成为左孩子,返回其父节点,(最右的那个不会满足此条件,但是head的father刚好为空,不影响)

题目3 序列化和反序列化
遍历树,

//先序遍历的序列化 和 反序列化
//序列化
public static String serialByPre(Node head) {
		if (head == null) {//#--空,!--分割,value--值
			return "#!";
		}
		String res = head.value + "!";
		res += serialByPre(head.left);
		res += serialByPre(head.right);
		return res;
	}
//反序列化。 分割
	public static Node reconByPreString(String preStr) {
		String[] values = preStr.split("!");
		Queue<String> queue = new LinkedList<String>();
		for (int i = 0; i != values.length; i++) {
		/*offer,add; poll,remove; peek,element
			前者返回空,后者异常
		*/
			queue.offer(values[i]);
		}
		return reconPreOrder(queue);
	}
//反序列化
	public static Node reconPreOrder(Queue<String> queue) {
		String value = queue.poll();
		if (value.equals("#")) {
			return null;
		}
		Node head = new Node(Integer.valueOf(value));
		head.left = reconPreOrder(queue);
		head.right = reconPreOrder(queue);
		return head;
	}

题目4 折纸问题
打印折痕方向 =》 凹 ,凸
题解
中序遍历即可
折纸_code

//space

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值