《Java程序员面试笔试宝典》读书笔记——链表常用操作

一、如何从链表中删除重复数据

  1. 把遍历的值存储到一个Hashtable中,利用其去重的特性。

    • 优点:时间复杂度低
    • 缺点:需要额外的存储空间来保存已遍历过的值
  2. 更为高效的办法:对链表进行双重循环遍历,外循环正常遍历链表,假设外循环当前遍历的结点为cur,内循环从cur开始遍历,若碰到与cur所指向结点值相同,则删除这个重复结点。

    • 优点:不需要额外的存储空间
    • 缺点:时间复杂度高
    • 算法如下:
 public void deleteDuplecate(Node head){
     Node p = head;
     while(p!=null){
         Node q = p;
         while(q.next!=null){
             if(p.data==q.next.data){
                 q.next = q.next.next;
             }else{
                 q = q.next;
             }
         }
         p = p.next;
     }
 }

二、如何找出单链表中的倒数第k个元素

  1. 设置两个指针,让其中一个指针比另一个指针先前移k-1步,然后两个指针同时往前移动。循环直到先行的指针值为NULL时,另一个指针所指的位置就是所要找的位置。代码如下:
public Node findElem(Node head,int k){
    if(k<1 || k>this.length())
        return null;
    Node p1 = head;
    Node p2 = head;
    for(int i=0;i1;i++)//前移k-1步
        p1 = p1.next;
    while(p1 != null){
        p1 = p1.next;
        p2 = p2.next;
    }
    return p2;
}

三、反转链表

  1. 我们知道,要反转链表,需要反转节点指针的方向,但是这过程中一旦调整了指针的指向,链表就断开了,因此我们需要在调整节点的next之前把下一个结点保存下来。代码:
public void ReverseIteratively(Node head){
    Node pReversedHead = head;
    Node pNode = head;
    Node pPre = null;
    while(pNode!=null){
        Node pNext = pNode.next;
        if(pNext==null)
            pReversedHead = pNode;
            pNode.next = pPre;
            pPre = pNode;
            pNode = pNext;
    }
    this.head = pReversedHead;
}

四、从尾到头输出单链表

  1. 可以采用上述方法,先反转单链表,再遍历
  2. 更好的办法:从头到尾遍历链表,没经过一个结点,把该结点放到一个栈中。当遍历完链表后,再从栈顶开始输出结点的值。
  3. 更好的办法:使用递归。每访问到一个结点,先递归输出它后面的结点,再输出该结点自身。代码:
public void printListReversely(Node pListHead){
    if(pListHead != null){
        printListReversely(pListHead.next);
        System.out.printlv(pListHead.data);
    }
}

五、寻找单链表的中间结点

  1. 先遍历一遍,求得链表的长度,然后再从头遍历length/2的距离即可找到链表的中间结点。
  2. 更好的办法:
    • 第一步,有两个指针同时从头开始遍历;
    • 第二步,一个快指针一次走两步,一个慢指针一次走一步;
    • 第三步,快指针先到链表尾部,而慢指针则恰好到达链表中部(如链表长度为奇数,慢指针指的即是链表中间指针,当链表长度为偶数时,慢指针指向的结点和慢指针指向结点的下一个结点都是链表的中间结点)。
public Node SearchMid(Node head){
    Node p = this.head;//快指针
    Node q = this.head;//慢指针
    while(p!=null&&p.next!=null&&p.next.next!=null){
        p = p.next.next;
        q = q.next;
    }
    return q;
}

六、如何检测一个链表是否有环

  1. 定义两个指针,一个快指针一次走两步,一个慢指针一次走一步。两个指针同时移动,快指针每移动一次都要跟慢指针比较,直到当快指针等于慢指针为止,就证明这个链表有环,否则,无环(快指针信道尾部为null,则无环)。代码:
public boolean IsLoop(Node head){
    Node fast = head;
    Node slow = head;
    if(fast==null){
        return false;
    }
    while(fast!=null&&fast.next!=null){
        fast = fast.next.next;
        slow = slow.next;
        if(fast==slow){
            return true;
        }
    }
    return !(fast==null||fast.next==null);
}

七、在不知道头指针的情况下删除指定结点

分两种情况来讨论:
1. 若待删除的结点为链表尾结点,则无法删除,因为删除后无法使其前驱结点的next指针置为空
2. 若待删除的结点不是尾结点,则可以通过交换这个结点与其后继结点的值,然后删除后继结点。代码:

public boolean deleteNode(Node n){
    if(n==null || n.next==null)
        return false;
    int tmp = n.data;
    n.data = n.next.data;
    n.next.data = tmp;
    n.next = n.next.next;
    return true;
}

你可能感兴趣的:(算法)