前言:当前内容只用来回忆复习,java编写,代码尽可能带注释,部分题目附上解题思路。如有错误,请指出,谢谢。
1、复杂链表的复制,全程没思路
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
1.1 用map保存旧节点和新节点之间的映射关系
步骤:遍历链表生成map映射新生成的结点,然后根据map组装新结点为链表
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
import java.util.Map;
import java.util.HashMap;
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
// 排除空指针
if(pHead == null)
return null;
RandomListNode head = new RandomListNode(pHead.label); // 待返回的链表头结点
// 创建map,map的键就是原链表结点,map的值就是待新生成的链表结点
// 先生产新链表的一个个结点,后根据map关系来组装新结点
Map<RandomListNode, RandomListNode> map = new HashMap<>();
RandomListNode p = pHead.next; // 辅助结点用来遍历原链表
while(p != null){
map.put(p, new RandomListNode(p.label) );
p = p.next;
}
// 组装结点
RandomListNode cur = pHead; // 原链表结点
p = head; // 辅助结点用来遍历新链表
while(cur != null){
p.next = map.get(cur.next);
p.random = map.get(cur.random);
p = p.next; // 新链表结点后移
cur = cur.next; // 原链表结点后移
}
return head;
}
}
1.2 三次遍历,先通过在每个结点后面添加完全相同的结点,生成新链表的next关系,再生成新链表的random关系,随后断开新旧链表
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
import java.util.Map;
import java.util.HashMap;
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
// 排除空指针
if(pHead == null)
return null;
//1、复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
while(currentNode != null){
RandomListNode cloneNode = new RandomListNode(currentNode.label);
RandomListNode nextNode = currentNode.next;
currentNode.next = cloneNode;
cloneNode.next = nextNode;
currentNode = nextNode;
}
currentNode = pHead;
//2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
while(currentNode != null) {
currentNode.next.random = currentNode.random==null?null:currentNode.random.next;
currentNode = currentNode.next.next;
}
//3、拆分链表,将链表拆分为原链表和复制后的链表,每次断两个连接
currentNode = pHead;
RandomListNode pCloneHead = pHead.next;
while(currentNode != null) {
RandomListNode cloneNode = currentNode.next;
currentNode.next = cloneNode.next;
cloneNode.next = cloneNode.next==null?null:cloneNode.next.next;
currentNode = currentNode.next;
}
return pCloneHead;
}
}
2、二叉树搜索与双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
2.1 先中序遍历得到结点数组,再更改数组中结点指针指向,最简单思路
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.ArrayList;
public class Solution {
public TreeNode Convert(TreeNode pRootOfTree) {
// 排除空指针
if(pRootOfTree == null)
return null;
// 先中序遍历二叉树,得到结点数组
ArrayList<TreeNode> list = new ArrayList<>();
InOrder(pRootOfTree, list);
// 遍历数组,更改结点左右指针
for(int i = 0; i < list.size()-1; i++){
list.get(i).right = list.get(i+1);
list.get(i+1).left = list.get(i);
}
return list.get(0);
}
public void InOrder(TreeNode root, ArrayList<TreeNode> list){
if(root == null)
return;
InOrder(root.left, list);
list.add(root);
InOrder(root.right, list);
return;
}
}
2.2 线索二叉树,线索二叉树就是排序二叉树的中序遍历结果,极其重要,必须掌握
中序遍历,保存前驱结点
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
TreeNode pre = null; // 保存前驱结点
TreeNode root = null; // 保存最左结点
public TreeNode Convert(TreeNode pRootOfTree) {
if (pRootOfTree == null)
return null;
Convert(pRootOfTree.left);
if (root == null){
// 保存最左结点
root = pRootOfTree;
}
// 最左边结点是没有前驱的,所以第一次遍历到最左边结点就保存为前驱
// 遍历到最左边结点的父结点时,更新前驱和父结点的指向关系
if (pre != null){
// 更改前驱结点与当前节点的指针指向
pRootOfTree.left = pre;
pre.right = pRootOfTree;
}
// 更新前驱
pre = pRootOfTree; //前驱
Convert(pRootOfTree.right);
return root;
}
}
3、字符串的排列,递归思想,再次注意递归传引用参数需要防止修改产生影响
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> list = new ArrayList<String>();
// 排除空指针或空字符串
if(str == null || str.length() == 0)
return list;
// 递归的思想分解为字符串的排列问题
StringBuilder sb = new StringBuilder(str);
list = PermHelp(sb);
// 排序
Collections.sort(list);
return list;
}
public ArrayList<String> PermHelp(StringBuilder sb){
ArrayList<String> list = new ArrayList<String>();
if(sb.length() == 1){
// 字符串长度为1时,说明就一个字母不需要考虑排列
list.add(sb.toString() );
return list;
}
for(int i = 0; i < sb.length(); i++){
// 将首字母和后面非重复字母交换,但“aa”至少有一次
if(i == 0 || sb.charAt(0) != sb.charAt(i) ){
// 先复制一份再交换,因为引用修改了,下次循环仍修改了
StringBuilder sbcopy = new StringBuilder(sb);
// 交换字母
Character tmp = sbcopy.charAt(0);
sbcopy.setCharAt(0, sbcopy.charAt(i) );
sbcopy.setCharAt(i, tmp);
// 切记,递归传入的参数如果是引用,退出递归最后不要产生干扰
// 将除去首字母的字符串继续递归
ArrayList<String> newList = PermHelp(new StringBuilder(sbcopy.substring(1) ) );
for(int j = 0; j < newList.size(); j++){
// 将首字母与递归返回结果遍历拼接
list.add(sbcopy.substring(0, 1) + newList.get(j) );
}
}
}
return list;
}
}