分解让复杂问题简单化
- 复杂链表的复制
- 二叉搜索树与双向链表
- 序列化二叉树
- 字符串的排列
35:复杂链表的复制
题目:
请实现函数复制一个复杂链表。在复杂链表中,每个节点除了有一个指针指向下一个节点,还有一个指针指向链表中的任意节点或者 null。
思路:
- 方法一,时间复杂度O(n2):
a. 复制原始链表上的每个节点,并用next 链接起来;
b. 设置每个节点的 random 指针。由于随机指针指向的位置可能在前面,也可能在后面,因此要从头节点开始找。 - 方法二,时间复杂度为O(n),但是需要一个大小为O(n) 的哈希表:
a. 复制原始链表上的每个节点,并用next 链接起来,同时把<被复制节点,复制节点>的配对信息放到哈希表中;
b. 设置每个节点的 random 指针,用哈希表,通过指针的key,查找复制节点。 - 方法三,
a. 复制每个节点,将复制节点插到被复制节点后面;
b. 重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
c. 拆分链表,将链表拆分为原链表和复制后的链表
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
if(head==null) return null;
Node curNode = head;
// 1. 遍历链表,复制每个节点,将复制节点插到被复制节点后面
while(curNode != null){
Node cloneNode = new Node(curNode.val);
cloneNode.next = curNode.next;
curNode.next = cloneNode;
curNode = cloneNode.next;
}
curNode = head;
//2. 重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
while(curNode != null){
curNode.next.random = curNode.random==null?null:curNode.random.next;
curNode = curNode.next.next;
}
curNode = head;
Node cloneHead = head.next;
//3. 拆分链表,将链表拆分为原链表和复制后的链表
while(curNode != null){
Node cloneNode = curNode.next;
curNode.next = cloneNode.next;
cloneNode.next = cloneNode.next==null?null:cloneNode.next.next;
curNode = curNode.next;
}
return cloneHead;
}
}
36:二叉搜索树与双向链表
题目:
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
思路:
• 节点从小到大排序,因此应使用中序遍历
• 在构建相邻节点(设前驱节点 prepre ,当前节点 curcur )关系时,pre.right = cur,且cur.left = pre
• 设链表头节点 head 和尾节点 tail ,则应构建 head.left = tail 和 tail.right = head
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val,Node _left,Node _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
Node head;
Node pre;
public Node treeToDoublyList(Node root) {
if(root == null) return null;
dfs(root);
head.left = pre;
pre.right = head;
return head;
}
private void dfs(Node node){
if(node==null)return;
dfs(node.left);
if(pre==null) head=node;
else pre.right = node;
node.left = pre;
pre = node;
dfs(node.right);
}
}
37:序列化二叉树
题目:
请实现两个函数,分别用来序列化和反序列化二叉树。
思路:
• 序列化 serialize:借助队列,对二叉树做层序遍历,并将越过叶节点的 null 也打印出来
• 反序列化 deserialize:“ node, node.left, node.right ” 在序列化列表中的位置关系,可实现反序列化。利用队列按层构建二叉树,借助一个指针 i 指向节点 node 的左、右子节点,每构建一个 node 的左、右子节点,指针 i 就向右移动 1 位
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root==null) return "[]";
StringBuilder res = new StringBuilder("[");
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node!=null) {
res.append(node.val + ",");
queue.add(node.left);
queue.add(node.right);
} else {
res.append("null,");
}
}
res.deleteCharAt(res.length() - 1);
res.append("]");
// System.out.println(res.toString());
return res.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.equals("[]")) return null;
String[] vals = data.substring(1, data.length() - 1).split(",");
TreeNode root = new TreeNode(Integer.parseInt(vals[0]));
int i = 1;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (!vals[i].equals("null")) {
node.left = new TreeNode(Integer.parseInt(vals[i]));
queue.add(node.left);
}
i++;
if (!vals[i].equals("null")) {
node.right = new TreeNode(Integer.parseInt(vals[i]));
queue.add(node.right);
}
i++;
}
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
38:字符串的排列
题目:
输入一个字符串,打印出该字符串中字符的所有排列。例如,输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
思路:
对于一个长度为 n 的字符串(假设字符互不重复),其排列共有 n×(n−1)×(n−2)…×2×1 种方案,根据字符串排列的特点,考虑深度优先搜索所有排列方案。即通过字符交换,先固定第 1 位字符( n 种情况)、再固定第 2 位字符(n−1 种情况)、……、最后固定第 n 位字符( 1 种情况);当字符串存在重复字符时,排列方案中也存在重复方案,为排除重复方案,需在固定某位字符时,保证 “每种字符只在此位固定一次” ,即遇到重复字符时不交换,直接跳过
class Solution {
public String[] permutation(String s) {
ArrayList<String> res = new ArrayList<>();
dfs(s.toCharArray(), res, 0);
return res.toArray(new String[res.size()]);
}
private void dfs(char[] chars, ArrayList<String> res, int i){
if(i == chars.length-1){
res.add(new String(chars));
return;
}
HashSet<Character> set = new HashSet<>(); //去重
for(int j=i;j<chars.length;j++){
if(set.contains(chars[j])) continue;
set.add(chars[j]);
swap(chars,i,j);
dfs(chars,res,i + 1);
swap(chars,i,j);
}
}
private void swap(char[] chars, int i, int j){
char t = chars[j];
chars[j] = chars[i];
chars[i] = t;
}
}