链表

面试时链表解题的方法论

1)对于笔试,不需要太在乎空间复杂度,一切为了时间复杂度

2)对于面试,时间复杂度依然在第一位,一定要找到空间最省的方法

重要技巧

1)额外数据结构记录(哈希表)

2)快慢指针

反转单向链表(时间复杂度O(N),额外空间复杂度O(1))

在这里插入图片描述
反转链表,链表变成D→C→B-→A
思路:
链表反转,A的next是B经过反转,B的next变成A,即A.next.next =A
定义两个变量一个是next 一个是pre。pre记录上一个节点信息,next记录下一个节点信息,
当前元素A向右走, next = A.next,A.next = pre, pre设置为A,当前元素进下一个。

非递归形式

	class ListNode {
	    int val;
	    ListNode next;
	 
	
	    ListNode(int x) {
	        val = x;
	        next = null;
	    }
	}

 private static ListNode reverseSingleList(ListNode head) {
        ListNode pre = null;
        ListNode next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            head.prev = next;
            head = next;
        }
        return head;
    }
递归形式

一直遍历走到最右端节点C,发现右节点C到头了,回到B,设置B节点next节点的next为B,将B的next设置为空等待下一个人帮他填上,到A节点了,将A节点的next的next设置为A,A的next设置为空等待下一个人帮他,但是A是原链表的头节点没人帮他了,遍历结束。

 private static ListNode reverseSingleList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode node = reverseSingleList(head.next);
        head.next.next = head;
        head.next = null;
        return node;
    }

反转单向链表(时间复杂度O(N),额外空间复杂度O(1))在这里插入图片描述

反转链表,链表变成D→C→B-→A
思路:
链表反转,A的next是B经过反转,B的next变成A,即A.next.next =A
定义两个变量一个是next 一个是pre。pre记录上一个节点信息,next记录下一个节点信息,
当前元素A向右走, next = A.next,A.next = pre, A.pre设置为next, pre设置为A,当前元素进下一个。

非递归形式
 private static ListNode reverseDoubleList(ListNode head) {
        ListNode pre = null;
        ListNode next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            head.prev = next;
            pre = head;
            head = next;
        }
        return head;
    }
递归形式
 private static ListNode reverseDoubleList(ListNode head) {
       while (head == null || head.next == null) {
           return head;
       }
       ListNode node = reverseDoubleList(head.next);
       
       ListNode next = head.next;
       next.next = head;
       head.prev = next;
       head.next = null;
       return node;
    }

打印两个有序链表的公共部分

【题目】 给定两个有序链表的头指针head1和head2,打印两个链表公共部分。

【要求】 如果两个链表长度之和为N,时间复杂度要求O(N),额外空间复杂度要求O(1)

有序链表,如果有公共部分,那么就需要比较两个链表的起始元素。需要A链表头元素与B链表元素大小,谁小谁向右移,如果一样大,一起移动,直到某个链表越界。

 private static void intersectionList(ListNode first, ListNode second) {
        while (first.next != null && second.next != null) {
            if (first.val == second.val) {
                first = first.next;
                second = second.next;
                System.out.println(first.val);
            } else if (first.val < second.val) {
                first = first.next;
            } else {
                second = second.next;
            }
        }
    }

#判断一个链表是否为回文结构

【题目】 给定一个单链表的头节点head,请判断链表是否为回文结构。

【例子】1→2→1,返回true;1→2→2→1,返回true;15→6→15返回true,1→2→3返回false

【要求】 如果链表长度为N,时间复杂度要求O(N),额外空间复杂度要求O(1)

思路:
笔试:
定义一个栈将元素都入栈,pop元素和链表从头节点到尾节点是否一致。

面试:

  1. 使用快慢指针,快指针走两步,慢指针走2步,快指针越界,慢指针走到中间位置。
  2. 将慢指针后next进行逆序
 private static boolean palindrome(ListNode node) {
        
        ListNode quick = node;
        ListNode slow = node;

        //快慢指针,快指针走两步,慢指针走一步,快指针走到头了,慢指针到中间

        /**
         * 1→2→3→2→1
         * 快指针走到1 、慢指针走到3 ;将3的next进行逆转变成1→2,比较
         * 
         * 1→2→3→3→2→1
         * 快指针走到1 、慢指针走到3 将3的next进行逆转变成1→2→3,比较
         */
        while (quick.next != null && quick.next.next != null) {
            quick = quick.next.next;
            slow = slow.next;
        }
        //逆序
        ListNode reverseNode = reverse(slow.next);
        while (reverseNode != null) {
            if (node.val != reverseNode.val) {
                return false;
            }
            reverseNode = reverseNode.next;
            node = node.next;
        }
        return true;
    }

将单向链表按某值划分成左边小、中间相等、右边大的形式

【题目】给定一个单链表的头节点head,节点的值类型是整型,再给定一个整 数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于pivot的 节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点。 。

【进阶】在实现原问题功能的基础上增加如下的要求 【要求】调整后所有小于pivot的节点之间的相对顺序和调整前一样

【要求】调整后所有等于pivot的节点之间的相对顺序和调整前一样

【要求】调整后所有大于pivot的节点之间的相对顺序和调整前一样

【要求】时间复杂度请达到O(N),额外空间复杂度请达到O(1)。

思路:
笔试的时候,将链表变成数组,对数组进行荷兰国旗问题。
面试的时候:

借助6个元素
LH 、LT 小于链表头节点 小于链表尾节点
EH 、ET 小于链表头节点 小于链表尾节点
MH 、MT 小于链表头节点 小于链表尾节点

 private static ListNode partitionList(ListNode node,
                                          int partition) {

        ListNode LH = null;

        ListNode LT = null;

        ListNode EH = null;

        ListNode ET = null;

        ListNode MH = null;

        ListNode MT = null;

        ListNode next = null;

        //将之前的next清除,链表逆序、排序、设计到顺序修改的问题一定要把next值出来,并将其next设置为null

        //非递归形式的修改链表顺序的,需要一个外部next节点作为游标,防止走乱套了
        while (node != null) {
            next = node.next;
            node.next = null;

            if (node.val < partition) {
                if (LH == null) {
                    LH = node;
                    LT = node;
                } else {
                    LT.next = node;
                    LT = node;
                }
            } else if (node.val > partition) {
                if (MH == null) {
                    MH = node;
                    MT = node;
                } else {
                    MT.next = node;
                    MT = node;
                }
            } else {
                if (EH == null) {
                    EH = node;
                    ET = node;
                } else {
                    ET.next = node;
                    ET = node;
                }
            }
            node = next;
        }

        //现在要做的是把三块区域拼接起来,可能三块某一块是空的
        
        //正常来说LT.next = EH
        //ET.next= MH
        if (LT != null) {
            LT.next = EH;
            //但是如果ET不存在,ST存在就需要ST直接和MH连,所以ST代替了ET位置。
            ET = ET == null ? LT : ET;
        }
        if (ET != null) {
            ET.next = MH;
        }
        return LH == null ? (EH == null ? MH : EH ): LH;
    }

复制含有随机指针节点的链表

【题目】一种特殊的单链表节点类描述如下

  class ListNode {
	    int val;
	    ListNode next;
	    ListNod rand;
	
	    ListNode(int x) {
	        val = x;
	        next = null;
	    }
	}

rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节 点,也可能指向null。给定一个由Node节点类型组成的无环单链表的头节点 head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。

【要求】时间复杂度O(N),额外空间复杂度O(1)

思路:
笔试 使用hash,key为链表节点 value为new出来的node,然后从map中获取random
面试:节点拷贝,当前节点向下拷贝一个,random就是正常的random的下一个节点,然后将两条链表分开。

public static Node copyListWithRand2(Node head) {

		if (head == null) {
			return null;
		}
		//记住一个事情,while循环遍历节点时增加一个cursor,
		//可以在外面和里面定义一个next,注意一定要有next
		Node cursor = head;
		while (cursor != null) {
			Node next = cursor.next;
			cursor.next = new Node(cursor.value);
			cursor.next.next = next;
			cursor = next;
		}

		//完成复制后进行完成复制random
		cursor = head;
		while (cursor != null) {
			Node copy = cursor.next;
			Node next = cursor.next.next;
			//需要判断next是有random
			copy.rand = cursor.rand == null ? null : cursor.rand.next;
			cursor = next;
		}

		//进行剥离两个链表
		cursor = head;
		Node copyHead = cursor.next;
		while (cursor != null) {
			Node next = cursor.next.next;
			Node copy = cursor.next;
			cursor.next = next;
			copy.next = next == null ? null : next.next;
			cursor = next;
		}
		return copyHead;
	}

两个单链表相交的一系列问题

【题目】给定两个可能有环也可能无环的单链表,头节点head1和head2。请实 现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返 回null

【要求】如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。

      无环单链表
  1. 两个单链表不相交
    在这里插入图片描述

  2. 两个单链表相交
    在这里插入图片描述

  3. 两个单链表肯定不会出现X型,因为对于链表来说,他不会有两个指针指向两个方向。

    有环单链表
    

    两个有环单链表相交,但是相交节点不一样
    在这里插入图片描述

    两个有环单链表不相交

在这里插入图片描述

两个有环单链表相交,但是相交节点一样
在这里插入图片描述

所以现在做的就是来确定他是否为有环链表,有环链表怎么区分:
快慢指针法
快指针走2步、慢指针走1步,如果快指针走到头了,next为null,那么他就是无环单链表。

	public static Node  acyclicSingleStrand(Node head) {
		
		if (head == null || head.next == null || head.next.next == null) {
			return null;
		}

		Node fast = head.next.next;
		Node slow = head.next;

		//如果有环快指针肯定会与慢指针有交集
		while (fast != slow) {
			//无环就是快指针后面没有节点了
			if (fast.next == null || fast.next.next ==null) {
				return null;
			}
			fast = fast.next.next;
			slow = head.next;
		}

		//如slow等于fast,如果快慢指针有交集,快指针回到第一个节点,慢指针不动,然后继续走
		//他俩第一个交集,就是第一个入环节点
		fast = head;
		while (fast != slow) {
			fast = fast.next;
			slow = fast.next;
		}
		return fast;
	}

如果两个链表都是无环单链表,判断二者是否有交集

public static Node bothSingle(Node head1, Node head2) {

		int cnt = 0;
		Node cursor = head1;

		//判断最后一个节点是否一样,如果一样则有环,不一样就是两个单独的单链表
		while (cursor.next != null) {
			cnt++;
			cursor = cursor.next;
		}

		cursor = head2;
		while (cursor.next != null) {
			cnt -- ;
			cursor = cursor.next;
		}

		//谁长谁就是node1
		Node node1 = cnt > 0 ? head1 : head2;
		//看看长的是不是head1,如果长的是head1,短的就是head2
		Node node2 = node1 == head1 ? head1 : head2;
		cnt = Math.abs(cnt);
		while (cnt > 0) {
			node1 = node1.next;
			cnt --;
		}
		
		//因为上面已经判断是否为孤零零的两个链表,答案是否,所以肯定有交集
		while (node1 != node2) {
			node1 = node1.next;
			node2 = node2.next;
		}
		return node1;
	}

如果是有环单链表,则需要判断两个链表入环节点是否一致 loop1 == loop2,如果一致的话,那么就是Y字型,与单链表求交集一致,如果不一致需要判断是否有交集,如果loop1 ,一直向后走,直到回到他本身都没有遇到loop2就说明这两个链表没有交集。

	// 两个有环链表,返回第一个相交节点,如果不想交返回null
	public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
		Node cur1 = null;
		Node cur2 = null;
		if (loop1 == loop2) {
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1) {
				n++;
				cur1 = cur1.next;
			}
			while (cur2 != loop2) {
				n--;
				cur2 = cur2.next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = Math.abs(n);
			while (n != 0) {
				n--;
				cur1 = cur1.next;
			}
			while (cur1 != cur2) {
				cur1 = cur1.next;
				cur2 = cur2.next;
			}
			return cur1;
		} else {
			cur1 = loop1.next;
			while (cur1 != loop1) {
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值