Java面试题链表、树和位操作

最近金九银十刚刚过去,可谓是有人欢喜有人愁,不过,今天的任务是跑不掉的,走到最后的才是胜利者,下面记录几个面试题

链表之快慢法

实现一个算法,找出单向链表中倒数第k个节点

太直观的解法我们就不去考虑了,我们考虑一下迭代法吧,使用两个指针P1和P2,将它们指向链表中相距K个结点的两个节点,具体做法是先将P1 和P2 指向链表首结点,然后将P2向前移动K个节点,然后以相同的速度移动这两个指针,P2 会在移动Length-k步后抵达链表尾结点,此时的P1 即指向倒数第K个节点

部分伪代码,时间O(n),空间O(1)

LinkedlistNode nthTolast(LinkedListNode head,int k){
    if(k<=0)
        return null;
    LinkedlistNode p1=head;
    LinkedlistNode p2=head;
    for(int i=0;i<k-1;i++){   //P2向前移动K个结点
        if(p2==null)
            return null;
        p2=p2.next;
        }
        if(p2==null)
            return null;
        while(p2.next!=null){    //P1和P2同时移动
            p1=p1.next;
            p2=p2.next;
        }
        return p1;
}

给定一个有环链表,实现一个算法返回环路的开头结点

这个题目就是典型的快慢法了,当在一个环形道路上,一辆车速度为a,另一辆车速度为2a,最终两辆车会碰到一起。(难道就不会出现刚好越过的情况吗??)别急,假设2a的车真的越过了a车,且a车处于位置i,那么2a车处于位置i+1,那么,在前一步,a车就处于位置i-1,2a车也处于位置i-1处,故还是碰到了一起。

这里写图片描述
有以上信息可以得出,碰撞点距离原点为K,则现在只需要再次将a车放在head,2a在碰撞点,都以a的速度前进,那么下次两车碰撞点就会在原点,此时返回原点指针即可。

伪代码

LinkedListNode FindBeginning(LinkedListNode head){
    LinkedListNode slow=head;
    LinkedListNode fast=head;
    while(fast!=null&&fast.next!=null){
        slow=slow.next;
        fast=fast.next.next;
        if(slow==fast){   //碰撞
            break;
        }
    }  
        if(fast==null||fast.next==null){   //错误检查,没有环路的情况
            return null;
        }
        slow=head;     //slow指向head,fast在碰撞点,
        while(slow!=fast){
            slow=slow.next;
            fast=fast.next;
        }
       return fast;
    }

实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个结点,其两棵子树的高度差不超过1.

从根节点递归向下检查每颗子树的高度。

伪代码

public static int checkHeight(TreeNode root){
    if(root==null)
        return 0;
    int leftHeight=checkHeight(root.left);
    if(leftHeight==-1)
        return -1;
    int rightHeight=checkHeight(root.right);
    if(rightHeight==-1)
        return -1;
    int heightDiff=leftHeight-rightHeight;
    if(Math.abs(heightDiff)>1)
        return -1;
    else
        return Math.max(leftHeight,rightHeight)+1;
}
public static boolean isBalanced(TreeNode root){
    if(checkHeight(root)==-1)
        return false;
    else
        return true;
        }

你有两颗非常大的二叉树:T1,有几百万个结点;T2,有几百万个结点。设计一个算法判断T2是否为T1的子树。
如果T1有这么一个结点n,其子树与T2一摸一样,则T2为T1的子树。也就是说,从结点n处把树砍断,得到的树与T2完全相同

解法一:如果结点比较少,自然使用字符串序列去进行匹配来的简单轻松,但是这里有几百万个结点,所以恐怕得另谋它法

解法二:搜遍较大的T1,每当T1的某个结点与T2的根节点匹配时,就调用treeMatch.treeMatch方法会比较两棵子树,检查是否相同,这个方法算是解法一的一个优化了,时间复杂度为O(n+k*m),n为T1的结点数,m为T2的结点数,k为T2根节点在T1中出现的次数。

伪代码

boolean containsTree(TreeNode t1,TreeNode t2){
    if (t2==null)    //t2为空,自然是t1的子树
        return true;
    return subTree(t1,t2);
}
boolean subTree(TreeNode r1,TreeNode r2){
        if(r1==null)
            return false;
        if(r1.data==r2.data){
            if(matchTree(r1,r2))
                return true;
        }
        return (subTree(r1.left,r2)||subTree(r1.right,r2));
}

boolean matchTree(TreeNode r1,TreeNode r2){
    if(r2==null&&r1==null)   //二者都为空,则互为子树
        return true;
    if(r1==null||r2==null)
        return false;
    if(r1.data!=r2.data)
        return false;
    return(matchTree(r1.left,r2.left)&&matchTree(r1.right,r2,right));
}

位操作

编写一个函数,确定需要改变几位,才能够将整数A转换成整数B

看似十分复杂,实际上只需要计算出两个数之间有哪些位不同,很简单使用异或操作(^)。

两种办法,第一个是从C的右边开始检测,第二个是从C的左边开始检测

int bitSwap(int a,int b){
    int count=0;
    for(int c=a^b;c!=0;c=c>>1){
        count+=c&1;      
        }
    return count;
}

或者

int bitSwap(int a,int b){
    int count=0;
    for(int c=a^b;c!=0;c=c&(c-1)){
        count++;
        }
    return count;
}

编写程序,交换某个整数的奇数位和偶数位(即位0与位1交换,位2与位3交换,。。。),使用指令越少越好

此题简单的方法就不讨论了,我们可以先将奇数位(偶数位)提出来统一移动一位,然后将二者的结构进行或运算即可。
提取奇数位:10101010(即0xAA)

int Swap(int x){
    return (((x&0xaaaaaaaa)>>1)|((x&0x55555555)<<1));

数组A包含0到n的所有整数,但其中缺少一个。数组A的元素都可以用二进制表示,唯一可用的访问操作是“从A[i]取出第j位数据”,该操作的时间复杂度为常数。请编写代码找出那个缺失的整数。时间复杂度要求为O(n)

有个类似的题目,只需要求和然后与完整的序列和相减,即可求出缺失的那个数,但此题也是可以这么做的,只是时间复杂度达不到要求。故而采取另外一种方式
解法:我们将眼光聚集在完整数组A中 每个数的最低位,可以发现统计最低位的个数,若0的个数=1的个数,则为偶数个,则若0的个数>1的个数,则为奇数个。换言之,缺少一个整数的数组A中,若最低位0的个数<=1的个数,则缺少的那个整数必为偶数,即它的最低位只能为0假设是奇数,那么数组A就是一个错误的数组;若最低位0的个数>1的个数,则缺少的那个整数最低位只能为1,因为完整数组A中最低位0的个数和1的个数相差不会超过1,所以最低位只能为1.
有了上面的结论,现在我们只需要通过逐位的筛选,最后将最低位到最高位组合起来,即可求得我们缺少的那个数了。

小结

Java的面试题是离不开数据结构的,说到底数据结构这种”高级编程语言“,是值得我们一生都去学习和研究的。

/* * 基于链表实现结构 */ package dsa; public class TreeLinkedList implements Tree { private Object element;//根节点 private TreeLinkedList parent, firstChild, nextSibling;//父亲、长子及最大的弟弟 //(单节点)构造方法 public TreeLinkedList() { this(null, null, null, null); } //构造方法 public TreeLinkedList(Object e, TreeLinkedList p, TreeLinkedList c, TreeLinkedList s) { element = e; parent = p; firstChild = c; nextSibling = s; } /*---------- Tree接口中各方法的实现 ----------*/ //返回当前节点中存放的对象 public Object getElem() { return element; } //将对象obj存入当前节点,并返回此前的内容 public Object setElem(Object obj) { Object bak = element; element = obj; return bak; } //返回当前节点的父节点;对于根节点,返回null public TreeLinkedList getParent() { return parent; } //返回当前节点的长子;若没有孩子,则返回null public TreeLinkedList getFirstChild() { return firstChild; } //返回当前节点的最大弟弟;若没有弟弟,则返回null public TreeLinkedList getNextSibling() { return nextSibling; } //返回当前节点后代元素的数目,即以当前节点为根的子的规模 public int getSize() { int size = 1;//当前节点也是自己的后代 TreeLinkedList subtree = firstChild;//从长子开始 while (null != subtree) {//依次 size += subtree.getSize();//累加 subtree = subtree.getNextSibling();//所有孩子的后代数目 } return size;//即可得到当前节点的后代总数 } //返回当前节点的高度 public int getHeight() { int height = -1; TreeLinkedList subtree = firstChild;//从长子开始 while (null != subtree) {//依次 height = Math.max(height, subtree.getHeight());//在所有孩子中取最大高度 subtree = subtree.getNextSibling(); } return height+1;//即可得到当前节点的高度 } //返回当前节点的深度 public int getDepth() { int depth = 0; TreeLinkedList p = parent;//从父亲开始 while (null != p) {//依次 depth++; p = p.getParent();//访问各个真祖先 } return depth;//真祖先的数目,即为当前节点的深度 } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值