剑指Offer Java版 链表

       链表,应该是在面试时被提及最频繁的数据结构,链表的结构很简单,由指针把若干个结点连接成连续结构,链表的创建,插入,删除等操作都只需要20行左右的代码就能实现,其代码量非常适合用来面试。此外,链表是一种动态的数据结构,其操作需要对指针进行操作,因此需要较好的编程功底才能写出完整的操作链表的代码,而且其非常灵活,可以用链表来设计具有挑战性的代码。

    面试题5 从尾到头打印链表

  输入一个链表,从尾到头打印链表每个节点的值。 

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/

        //由于需要的是从尾到头的链表结构,即最先遍历的最后输出,那么一个很自然的想法应该是栈
        //同时,递归的本质即为栈,所以可以使用递归的思想。
        //但是采用递归内存超出,而且当数据足够大的时候会出现栈溢出。

这里采用栈的方式实现

public void printListFromTailToHead(ListNode head) {
		Stack stack = new Stack();
		ListNode node = head;
		while (node != null) {
			stack.push(node);
			node = node.next;
		}
		while (!stack.isEmpty()) {
			node = stack.pop();
			System.out.println(node.value);

		}

	}

面试题 13 在O(1) 的时间内删除结点

给定单向链表的头指针和一个结点指针,定义一个函数在O(1)的时间内删除该结点


思路:在单向链表中删除结点,最常规的做法无非就是遍历链表,找到要删除的结点,但是该方法的时间复杂度是O(n),不符合题目的要求。那么我们换一种思路,,我们可以很方便的删除该结点的下一个结点,如果我们把下一个结点的内容给复制到要删除的结点上,并且删除下一个结点,那是不是就相当于把当前需要删除的结点删除了?

但是还有一个问题就是,如果要删除的结点位于链表的尾部,那么他没有下一个结点,怎么办?这种情况下,我们仍然需要遍历到该结点的前序结点,并完成删除操作。

public void deleteNode(ListNode head, ListNode toBeDelNode){
		if( head ==null || toBeDelNode ==null){
			return ;
		}
		//不是尾结点
		if( toBeDelNode.next !=null){
			ListNode next = toBeDelNode.next;
			toBeDelNode.value = next.value;  
			toBeDelNode.next = next.next;
			next = null ;
		}
		//此时对应的情况是只有一个结点,此时需要把头结点置空
		else if(head == toBeDelNode){
			toBeDelNode = null;
			head =null ;
		}else{
			//删除尾结点
			ListNode node = head;
			while( node.next != toBeDelNode){
				node = node.next;
			}
			node.next = null ;
			toBeDelNode =null ;
		}
	}

面试题15 ,链表中的倒数第k个结点

链表的尾结点为倒数第一个结点,输出倒数第k个结点。

这题的常规思路就是遍历链表两次,但是还有更好的解法,为了实现找到倒数第k个结点,我们需要定义两个指针,第一个指针从链表的头指针开始先走k-1 步,之后两个指针一起走,当第一个指针到达尾结点的时候,此时第二个指针恰好指向倒数第k个结点

 public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null || k<=0){
            return null;
        }
        //两个指针,一个快一个慢
        ListNode quick=head;
        ListNode slow=head;
        for(int i=1;i
其实,这种有两个指针的思想不单单适用于解决这种问题,还可以解决其他问题,比如要判断链表是否有环,也可以使用两个指针,一个一次走两步,一个一次走一步,当慢的追上快的之后,就证明有环。如果快指针走到末尾都没有追上,则证明无环。此外,还可以用来求链表中结点,也是两个指针, 一个一次走两步,一个一次走一步。当快指针走到末尾的时候,慢指针所指向的就是中间的结点。


面试题16 . 反转链表 

 输入一个链表,反转链表后,反转链表,并返回反转链表的头结点。

 这道题只要把逻辑处理好即可

public ListNode ReverseList(ListNode head) {
        //如果链表为空,或者链表中的元素只有一个,返回head即可
        if(head==null || head.next==null){
            return head;
        }
        ListNode pre=null;   //前一个结点
        ListNode nextNode=null;   //后一个结点
        while(head!=null){
            nextNode = head.next;  //后一个结点赋值
            head.next = pre;    //反转
            pre=head;      //前一个结点指向当前结点
            head=nextNode; //移动当前的结点

        }
        return pre;
    }

       链表的题目大多不太复杂,但是有的题目考察思维能力,比如在O(1)时间内删除结点,如果没有想到使用后面的结点的值覆盖前面的值,再删除后面的结点,那么就很难想到怎么做。所以,一定要扩宽自己的思维






你可能感兴趣的:(剑指Offer)