常见的面试编程题--链表+树

通过递归实现创建一个链表

Node节点的定义

public class Node {
    private final int value;
    private Node next ;
    public Node(int value)
    {
        this.value = value;
        next = null;
    }

    public int getValue() {
        return value;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

    public static void printLinkedList(Node node)
    {
        while (node!=null)
        {
            System.out.print(node.getValue());
            System.out.print(" ");
            node = node.getNext();

        }
        System.out.println();

    }
}

递归创建链表

public Node createLinkedList(List<Integer> data)
    {
        if (data.isEmpty())
        {
            return  null;
        }
        Node firstNode = new Node(data.get(0));
        firstNode.setNext(createLinkedList(data.subList(1,data.size())));
        return firstNode;
    }

给定value值删除链表中出现的所有节点,存在多个相同节点的情况。

注意点:

  • 头节点就出现匹配的数据,而且不止一个
  • 删除节点的思想:改变next的指向
  • 找出共同处理的地方使用循环的思想解决
    public Node deleteIfValue(Node head,int value){

        //特殊情况,头节点存在匹配value的情况
        while (head!=null&&head.getValue()==value)
        {
            head = head.getNext();
        }
        if (head==null)
        {
            return null;
        }
        //定义出一个指针
        Node pre = head;
        while (pre.getNext()!=null)
        {
            if (pre.getNext().getValue()==value)
            {
                pre.setNext(pre.getNext().getNext());
            }else {
                pre = pre.getNext();
            }
        }
        return head;
    }

利用两个栈实现一个队列的操作

栈的存储特点是后进先出,所以两个栈一个作为压栈使用,另一个作为弹出,但是要注意的一点就是需要保证栈1的元素都完全倒入到栈2中。

两个各司其职的栈
  • 如果stackPush要往stackPop中压入数据,那么久必须把stackPush中的元素全部压入。
  • 如果stackPop中不为空,那么就不可以往stackPop压入元素。
public class StackQueue {

    public Stack<Integer>   stackPush;
    public Stack<Integer>   stackPop;

    public StackQueue()
    {
        stackPush = new Stack<Integer>();
        stackPop = new Stack<Integer>();
    }

    //队列进队
    public void add(Integer i)
    {
        stackPush.push(i);
    }

    //队列的poll方法
    public int poll()
    {
        if (stackPop.empty()&&stackPush.empty())
        {
            System.out.println("队列为空。");
        }
        else if (stackPop.empty())
        {
            while (!stackPush.empty())
            {
                stackPop.push(stackPush.pop());
            }

        }
        return  stackPop.pop();

    }

    //队列的peek方法
    public int peek()
    {
        if (stackPop.empty()&&stackPush.empty())
        {
            System.out.println("队列为空。");
        }else if (stackPop.empty())
        {
            while (!stackPush.empty())
            {
                stackPop.push(stackPush.pop());
            }
        }
        return stackPop.peek();
    }
}

判断一个链表是不是回文的结构

题目:给定一个链表的头结点head,请判断该链表是不是回文的结构?
解决思路:利用栈的存储后进先出的特点进行回文的判定

//链表节点的定义
class LinkedNode
{
    int value;
    LinkedNode next = null;
    LinkedNode(int value)
    {
        this.value = value;
    }
}
    public boolean isPalindrome(LinkedNode head)
    {
        if (head==null){
            return  false;
        }
        Stack<Integer> stack = new Stack<Integer>();
        LinkedNode cur = head;
        //将链表的数据压入栈中
        while (cur!=null)
        {
           stack.push(cur.value);
           cur = cur.next;
        }

        //出栈进行回文判断
        while (head!=null)
        {
            if (head.value!=stack.pop())
            {
                return false;
            }
            head = head.next;
        }

        return true;
    }

删除一个链表中重复出现的节点

题目: 给定一个无序单链表的头节点head,删除其中值重复出现的节点。
例如:1->2->3->3->4->4->2->1->1->null,删除值重复的节点之后为
1->2->3->4->2->1->null

解决思路:

  • 生成一个哈希表,因为头节点是不用删除的节点,所以首先将头节点的值存放入哈希表
  • 从头节点的下一个节点开始往后遍历节点,假设当前遍历到cur节点,先检查cur的值是否在哈希表中,如果在,则说明cur节点的值是之前出现过的,就将cur节点删除,删除的方式是将最近一个没有被删除的节点pre连接到cur的下一个节点,即pre.next = cur.next.如果不在,将cur节点的值加入哈希表,同时令pre=cur,即更新最近一个没有被删除的节点。
    public void DeleteListNode(LinkedNode head)
    {
        //如果头结点是空的话结束程序
        if (head==null)
        {
            return ;
        }

        //初始化
        LinkedNode pre = head;
        LinkedNode cur = head.next;

        HashSet<Integer> set = new HashSet<Integer>();
        set.add(head.value);
        while (cur!=null)
        {
            if (set.contains(cur.value))
            {
                pre.next = cur.next;
            }
            else
            {
                set.add(cur.value);
                pre = cur;
            }
            cur = cur.next;
        }

打印两个有序链表的公共节点

题目:提供两个有序链表的头指针head1和head2,打印两个链表的公共部分。

    public void print(LinkedNode head1,LinkedNode head2)
    {
        while (head1!=null&&head1!=null)
        {
            if (head1.value<head2.value)
            {
                head1=head1.next;
            }else if (head1.value>head2.value)
            {
                head2=head2.next;
            }else
            {
                System.out.println(head1.value);
                head1 = head1.next;
                head2 = head2.next;
            }
        }

    }

反转链表

题目:输入一个链表,反转链表后,输出链表的所有元素。

解题思路:

  • 当前节点是head,pre为当前节点的前⼀节点,next为当前节点的下⼀节点
  • 需要pre和next的⽬目的是让当前节点从pre->head>next1->next2变成pre<-head next1->next2
  • 先保存head.next的节点,然后将head.next=pre换成前一个节点的引用
  • 然后pre = head,head = next,进行节点的遍历替换
  • 如果head为null的时候,pre就为最后一个节点了了,但是 链表已经反转完毕,pre就是反转后链表的第一个节点
    public LinkedNode reverse(LinkedNode head)
    {

        if (head==null)
        {
            return null;
        }

        //初始化前后指标节点
        LinkedNode pre = null;
        LinkedNode next = null;
        while (head!=null)
        {
            next = head.next;
            head.next = pre;
            //head--> null

            pre = head;
            head = next;
        }

        return pre;
    }

输入一个链表,输出该链表中倒数第k个结点。

  • 这道题的难点在于不不知道k结点的位置。
  • 我们可以使⽤用两个指针,第一个指针先跑k-1 ,然后两个指针再一起跑
  • 等到第一个指针到终点时,第二个指针距终点也就是倒数第k个结点

定义两个指针,我们都知道倒数第k个距离最后一个的距离是k-1,所以可以先移动一个指针走k步后,然后两个指针同时移动,那么在快的指针到达结尾时,慢的指针到达的位置正好是倒数第k个。

    public LinkedNode FindKthNode(LinkedNode head,int k)
    {

        //定义两个指针
        LinkedNode point1 = head;
        LinkedNode point2 = head;

        int a = k;//记录k值的变化
        int count = 0;//记录节点的数目


        while (point1!=null)
        {
            point1 = point1.next;
            count++;
            if (k<1) //说明此事point1已经到正向k的位置
            {
                point2 = point2.next; //指针2开始
            }
            k--; //point1每次走一次都要减去1
        }

        if (count<a)
            //如果节点个数⼩小于所求的倒数第k个节点,则返回null
        {
            return null;
        }

        return  point2;
    }

附加

二分查找

注意点:

  • mid的计算方式采用a+(b-a)/2
  • 理由是:当a和b的值过大的情况会出现溢出的情况,所以这里采用改进
  public static int binSearch(int[] arr,int k){
        int low = 0;
        int high = arr.length;
        //注意high的取值是arr.length或arr.length-1,对于循环体的处理不同
        while (high>low)
        {
            int mid = low+(high-low)/2; //(low+high)/2 当两个数很大的情况可能出现溢出
            if (arr[mid]>k)  // [low,mid)
            {
                high = mid;
            }else if (arr[mid]<k)
            {
                low = mid+1;
            }else 
                  return mid;
        }
        return -1;
    }

树的定义

package com.joyo.code;

public class TreeNode {
    private final char value;
    private TreeNode left;
    private TreeNode right;

    public TreeNode(char value) {
        this.value = value;
        this.left = null;
        this.right = null;
    }

    public char getValue() {
        return value;
    }

    public TreeNode getLeft() {
        return left;
    }

    public void setLeft(TreeNode left) {
        this.left = left;
    }

    public TreeNode getRight() {
        return right;
    }

    public void setRight(TreeNode right) {
        this.right = right;
    }
}

创建树

二叉树
public TreeNode treeCreator()
    {
        TreeNode root = new TreeNode('A');
        root.setLeft(new TreeNode('B'));
        root.getLeft().setLeft(new TreeNode('D'));
        root.getLeft().setRight(new TreeNode('E'));
        root.getLeft().getRight().setLeft(new TreeNode('G'));
        root.setRight(new TreeNode('C'));
        root.getRight().setRight(new TreeNode('F'));
        return root;
    }

树的遍历(前序、中序、后序)

主要还是通过递归的思想来进行遍历,前中后就是调整语句的执行顺序

前序

 //前序遍历
    public static void  preOrder(TreeNode root){
        if (root==null)
        {
            return;
        }
        System.out.print(root.getValue());
        preOrder(root.getLeft());
        preOrder(root.getRight());
    }

中序

  //中序遍历
    public void inOrder(TreeNode root){
        if (root==null)
        {
            return;
        }
        inOrder(root.getLeft());
        System.out.print(root.getValue());
        inOrder(root.getRight());
    }

后序

//后续遍历
    public void postOrder(TreeNode root){
        if (root==null)
        {
            return;
        }
        postOrder(root.getLeft());
        postOrder(root.getRight());
        System.out.print(root.getValue());
    }

输出结果:
前序:ABDEGCF
中序:DBGEACF
后序:DGEBFCA

根据前序中序输出后序

注意点:

  • 缩小范围,寻找递归的执行区域
  • 前序和中序可以确认树的结构,但是前序和后序无法唯一性确定好二叉树的结构


    递归缩小求解范围
public String postPrint(String pre,String in){
        if (pre.isEmpty()){
            return "";
        }
        char rootNode = pre.charAt(0);
        int indexNode = in.indexOf(rootNode);
        return postPrint(pre.substring(1,indexNode+1),in.substring(0,indexNode))+
                postPrint(pre.substring(indexNode+1),in.substring(indexNode+1))+
                rootNode;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 什么是数组? 数组简单来说就是将所有的数据排成一排存放在系统分配的一个内存块上,通过使用特定元素的索引作为数组的下...
    启明_b56f阅读 845评论 0 0
  • //leetcode中还有花样链表题,这里几个例子,冰山一角 求单链表中结点的个数----时间复杂度O(n)这是最...
    暗黑破坏球嘿哈阅读 1,471评论 0 6
  • 搞懂单链表常见面试题 Hello 继上次的 搞懂基本排序算法,这个一星期,我总结了,我所学习和思考的单链表基础知识...
    醒着的码者阅读 4,524评论 1 45
  • 寂寂空山路冷清,丛丛野菊笑相迎。 寒香素淡凝朝露,霜蕊恬和蕴晚晴。 不与春花争烂漫,却教秋蝶独钟情。 豪放独绽啸山...
    逸塵居士阅读 420评论 0 0
  • 在这里想先解释一下为什么(只)考虑性别差异。 虽说男女之间其实相同远多于差异(心理课上老师们也强调过“火星金星”之...
    潜_921f阅读 651评论 0 0