剑指offer

15 链表中倒数第k个结点

是否环形链表
假设存在两个指针*a,*b都指向链表的头结点,每循环判断一次,a向前走一步,b向前走两步。那么如果这样算下去,总有一个有限循环内,b会到达NULL指针或者b和a相等。此时停止循环。

//判断链表是否存在环  
public class Solution {
    public boolean hasCycle(ListNode head) {

        if (head == null) return false;
        ListNode first = head, second = head;
        while (first != null && first.next != null) {
            first = first.next.next;
            second = second.next;
            if (first == second) return true;
        }
        return false;
    }
}

16 反转链表

//递归方式
Node * reverseList(List head)
{
    //如果链表为空或者链表中只有一个元素
    if(head == NULL || head->next == NULL)
    {
        return head;
    }
    else
    {
        //先反转后面的链表,走到链表的末端结点
        Node *newhead = reverseList(head->next);
        //再将当前节点设置为后面节点的后续节点
        head->next->next = head;
        head->next = NULL;

        return newhead;
    }
}

这里写图片描述

19 二叉树的镜像

//递归方式
public void mirror(Node root) {
    if (root == null || (root.left == null && root.right == null)) return;
    Node temp = root.left;
    root.left = root.right;
    root.right = temp;
    if (root.left != null) mirror(root.left);
    if (root.right != null) mirror(root.right);
}
//非递归方式
public void mirror(Node root) {
    if (root == null || (root.left == null && root.right == null)) return;
    LinkedList<Node> list = new LinkedList<>();
    list.addFirst(root);
    Node temp = null;
    Node current = null;
    while (!list.isEmpty()) {
        current = list.removeFirst();
        if (current.left != null || current.right != null) {
            temp = current.left;
            current.left = current.right;
            current.right = temp;
        }
        if (current.left != null) list.addFirst(current.left);
        if (current.right != null) list.addFirst(current.right);

    }
}

26 复杂链表的复制
分为三步,第一步把原链表的每个节点都复制一个,并放在原每个节点后面;第二步,设置另一个指针;第三步,拆分两个链表
牛客上没有通过

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    public RandomListNode Clone(RandomListNode pHead)
    {

        if (pHead != null) {
            copyNode(pHead);
            copyRandom(pHead);
            return split(pHead);
        }
        return null;


    }

    private void copyNode(RandomListNode pHead) {
        while (pHead != null) {
            RandomListNode n = new RandomListNode(pHead.label);
            n.next = pHead.next;
            pHead.next = n;
            pHead = n.next;
        }
    }

    private void copyRandom(RandomListNode pHead) {
        while (pHead != null) {
            RandomListNode n = pHead.next;
            n.random = pHead.random.next;
            pHead = n.next;
        }
    }

    private RandomListNode split(RandomListNode pHead) {
        RandomListNode n = pHead.next;
        pHead = n;
        while (pHead.next != null) {
            pHead.next = pHead.next.next;
            pHead = pHead.next;
        }
        return n;
    }
}

27 二叉树转双向链表
由于二叉搜索树的中序遍历是递增序列,所以这个题考虑用中序遍历的方式。二叉树的根节点需要和左子树最大的一个节点相连,同时也需要和右子树最大的一个节点相连。所以需要一个指针依次指向二叉树的左节点、根节点和右节点。当这个指针指向左节点时,由于递归会使得栈里的“指针”指向根节点,因为此时左节点是叶子节点,所以递归会return到根节点;然后就可以让这个指向左节点的指针和指向根节点的“指针”完成互相指向的操作,即左节点右边是根节点,根节点左边是左节点。然后当前节点指向根节点,递归去做右子树的转化操作。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public TreeNode current = null;
    public TreeNode head = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        convertNode(pRootOfTree);
        return head;
    }

    public void convertNode(TreeNode root) {
        if (root == null) return;
        convertNode(root.left);
        if (current == null) { //初始化
            current = root;
            head = root;
        } else {
            current.right = root;
            root.left = current;
            current = root;
        }
        convertNode(root.right);
    }
}

28 字符串的全排列
主要是通过交换字符的位置完成排列,假设原字符串为abc
a不交换位置,即相当于a与a交换位置,这时固定好a,后面的bc然后重复这一过程,很明显,这是一个递归的思路。这完成了由a打头的所有字符串的排列。
a与b交换位置,字符串变为bac。同理,这将完成所有由b打头的字符串的排列。
a与c交换位置,这将完成所有由c打头的字符串的排列。

import java.util.*;
public class Solution {
    public List<String> permutation(String str) {
        List<String> list = new ArrayList<>();
        if (str != null && str.length() > 0) {
            permutation(str.toCharArray(), 0, list);
            Collections.sort(list);
        }
        return list;
    }

    private void permutation(char[] arr, int index, List<String> list) {
        if (index == arr.length - 1) {
            list.add(String.valueOf(arr));
        } else {
            for (int i = index; i < arr.length; i++) {
                if (i == index || arr[i] != arr[index]) {
                    swap(arr, i, index);
                    permutation(arr, index + 1, list);
                    swap(arr, i, index);
                }
            }
        }
    }

    private void swap(char[] arr, int i, int j) {
        char tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

29 数组中出现次数超过一半的数字
使用HashMap存储数字和出现的次数。注意数组只有一个元素时这样的边界条件。

30 最小的k个数
1. 使用最大堆,自己写堆排序的代码
2. 使用TreeMap,基于红黑树,查找、插入和删除时间都是logN。使用TreeMap的lastKey方法。因为红黑树是二叉查找树,只要比最大的小就ok。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值