链表的常见面试题

先说下一个节点的结构:一个val域和一个next域

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

一、已知链表的头节点,将链表进行逆序。(不可以申请额外的空间)

public static ListNode reverseList(ListNode node) {
	ListNode pre = null;
	ListNode pNext = null;
	while(node != null) {
		pNext = node.next;
		node.next = pre;
		pre = node;
		node = pNext;
	}
	return pre;
}

二、已知链表的头节点,将链表从位置m到n逆序。注意:1<=m<=n<=链表长度(不允许申请额外空间)

思路:主要是要找到逆序的头节点和尾巴,以及头节点的前驱节点,和尾节点的后置节点

注意:如果m=1,说明是从第一个节点开始,那么就需要修改头节点

public static ListNode reverseListBetween(ListNode node,int m,int n) {
	int index = 1;
	ListNode p = node;
	ListNode pre = null;
	while(index <= n) {
		if(index == m) {
			ListNode temp = p;
			ListNode tempPre = null;
			ListNode tempNext = null;
			for(int i = m;i <= n;i++) {//开始进行节点的逆序
				tempNext = temp.next;
				temp.next = tempPre;
				tempPre = temp;
				temp = tempNext;
			}
			if(pre == null) {//说明是从头节点就开始翻转
				p.next = temp;
				node = tempPre;//修改下头节点
			}else {
				pre.next = tempPre;
				p.next = temp;
			}
			index = n;
		}
		pre = p;
		p = p.next;
		index++;
	}
	return node;
}

三、求两个链表公共的节点,若没有则返回null

解题思路:给个图就能知道!!!!从相同的位置开始判断。交点之后都是相同的。

链表的常见面试题_第1张图片

public static ListNode getIntersectionNode(ListNode node1,ListNode node2) {
	//先求两个链表的长度
	int len1 = getLengthOfList(node1);
	int len2 = getLengthOfList(node2);
	//将指针移动到相同的位置
	if(len1 > len2) {
		for(int i = 0; i < len1 - len2;i++) 
			node1 = node1.next;
	}else {
		for(int i = 0; i < len2 - len1;i++) 
			node2 = node2.next;
	}
	//找相同的节点
	while(node1 != null && node2 != null) {
		if(node1 == node2)
			return node1;
		node1 = node1.next;
		node2 = node2.next;
	}
	return null;
}
public static int getLengthOfList(ListNode node) {
	int len = 0;
	ListNode p = node;
	while(p != null) {
		len++;
		p = p.next;
	}
	return len;
}

四、判断一个链表中是否有环,若有则返回环的入口,若没有则返回null

链表的常见面试题_第2张图片

思路:设置快慢指针。

public static ListNode detectCycle(ListNode node) {
	ListNode fast = node;
	ListNode low = node;
	while(low!=null && fast!=null && low.next != null && fast.next !=null) {
        low = low.next;
		fast = fast.next.next;
		if(low == fast) {
			//设置一个指针从头节点开始
			ListNode temp = node;
			while(temp != low) {
				temp = temp.next;
				low = low.next;
			}
			return temp;
		}
	}
	return null;
}

五、链表划分:给定一个值x,将所有小于x的节点放在大于或等于x的节点前,且要保持这些节点原来的相对位置

链表的常见面试题_第3张图片

解题思路:若是数组的话,实际上修改下排序的规则,并选择一种稳定的排序即可,但是链表采用排序的方式就会稍微复杂。实际上可以设置两个节点,遍历链表,将小于x的节点连接到一个节点,将大于或等于x的节点连接到另外一个节点,最后再将两个链表合并。

public static ListNode partition(ListNode node,int val) {
	ListNode A = new ListNode(val);
	ListNode B = new ListNode(val);
	ListNode p1 = A;
	ListNode p2 = B;
	while(node != null) {
		if(node.val < val) {
			p1.next = node;
			p1 = p1.next;
		}else {
			p2.next = node;
			p2 = p2.next;
		}
		node = node.next;
	}
	p1.next = B.next;
	p2.next = null;
	return A.next;
}

六、复杂链表的复制。

结点结构有三部分:一个值域 一个next域,一个随即域 ,指向任意节点。

public class RandomListNode {
	int val;
	RandomListNode next = null;
	RandomListNode random = null;
	public RandomListNode(int val) {
		this.val = val;
	}
}

 

链表的常见面试题_第4张图片

public static RandomListNode copyRandomListNode(RandomListNode node) {
	if(node == null)
		return null;
	//先复制节点
	RandomListNode p = node;
	while(p != null) {
		RandomListNode temp = new RandomListNode(p.val);
		temp.next = p.next;
		p.next = temp;
		p = p.next.next;
	}
	//复制节点的指向
	p = node;
	while(p != null) {
		if(p.random != null) {
			p.next.random = p.random.next;
		}
		p = p.next.next;
	}
	//拆分
	p = node;
	RandomListNode head = p.next;
	RandomListNode t = head;
	while(t.next != null) {
		p.next = t.next;
		t.next = t.next.next;
		p = p.next;
		t = t.next;
	}
	p.next = null;
	return head;
}

七、两个排序链表的合并,合并后任然是有序的

public static ListNode mergeTwoLists(ListNode node1,ListNode node2) {
	if(node1 == null)
		return node2;
	if(node2 == null)
		return node2;
	ListNode p = null;
	if(node1.val < node2.val) {
		p = node1;
		node1 = node1.next;
	}else {
		p = node2;
		node2 = node2.next;
	}
	ListNode t = p;
	while(node1 != null && node2 != null) {
		if(node1.val < node2.val) {
			t.next = node1;
			node1 = node1.next;
		}else {
			t.next = node2;
			node2 = node2.next;
		}
		t = t.next;
	}
	//退出时,至少有一个链表结束遍历了
	if(node1 != null) {
		t.next = node1;
	}
	if(node2 != null) {
		t.next = node2;
	}
	return p;
}

八、链表的k逆序问题:每k个节点之间进行逆序,最后不足k个节点不逆序

链表的常见面试题_第5张图片

public static ListNode reverseK(ListNode node,int k) {
	//判断是否有k个节点,不足k个节点  直接返回头节点
	int index = 0;
	ListNode p = node;
	while(p != null) {
		index++;
		p = p.next;
	}
	if(index < k)
		return node;
	p = node;
	ListNode temp = p;//保存下当前的头节点
	ListNode pre = null;
	ListNode pNext = null;
	for(int i = 0; i < k;i++) {
		pNext = p.next;
		p.next = pre;
		pre = p;
		p = pNext;
	}
	//新的头节点为pre;
	temp.next = reverseK(p,k);//当前k个节点的尾节点的下一个为下k个节点逆序后的头节点
	return pre;
}

九、删除链表中指定的值,返回清除后的头节点

注意:是否删除的头节点

public static ListNode clearList(ListNode node,int val) {
	ListNode p = node;
	ListNode pre = null;
	while(p != null) {
		if(p.val == val) {
			//判断是否是头节点
			if(p == node) {
				node = p.next;
				p = node;
			}else {
				pre.next = p.next;
				p = p.next;
			}
		}else {
			pre = p;
			p = p.next;
		}
	}
	return node;
}

十、判断链表是否是回文

public static boolean isPalindrome(ListNode node) {
	ArrayList list = new ArrayList<>();
	ListNode p = node;
	while(p != null) {
		list.add(p);
		p = p.next;
	}
	for(int i = 0; i < list.size()/2;i++) {
		if(list.get(i).val != list.get(list.size()-1-i).val)
			return false;
	}
	return true;
}

感觉差不多了   反正链表很简单  就这样吧

你可能感兴趣的:(数据结构,链表,常见面试题)