剑指offer_分解

前言:当前内容只用来回忆复习,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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值