链表知识点总结
(1)单链表
(2)双向链表
(3)循环链表
链表题目总结
(1)从尾到头打印链表
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
//利用栈来实现
import java.util.Stack;
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack=new Stack<Integer>();
while(listNode!=null){
stack.push(listNode.val);
listNode=listNode.next;
}
ArrayList<Integer> list=new ArrayList<Integer>();
while(!stack.isEmpty()){
list.add(stack.pop());
}
return list;
}
}
(2)链表中倒数第k个节点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(null == head)
{
return null;
}
if(k <=0)
{
return null;
}
ListNode ptr = head;
for(int i = 0; i < k - 1; i++)
{
ptr = ptr.next;
}//for
if(null == ptr)
{
return null;
}//if
ListNode pcur = head;
while(null != ptr.next)
{
ptr = ptr.next;
pcur = pcur.next;
}//while
return pcur;
}
}
(3)翻转链表
输入一个链表,反转链表后,输出新链表的表头。
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head==null){
return null;
}
if(head.next==null){
return head;
}
ListNode pre=head;
ListNode mid=head.next;
ListNode hehide=mid.next;
while(hehide!=null){
mid.next=pre;
pre=mid;
mid=hehide;
hehide=hehide.next;
}
mid.next=pre;
pre=mid;
mid=hehide;
head.next=null;
return pre;
}
}
(4)合并两个链表的排序
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
ListNode head=new ListNode(-1);
ListNode mergeHead=head;
while(list1!=null&&list2!=null){
if(list1.val<=list2.val){
mergeHead.next=list1;
list1=list1.next;
}
else{
mergeHead.next=list2;
list2=list2.next;
}
mergeHead = mergeHead.next;
}
if (list1 != null) mergeHead.next = list1;
if (list2 != null) mergeHead.next = list2;
return head.next;
}
}
//也可以采取递归的办法,参考牛客网上的答案
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1 == null){
return list2;
}
if(list2 == null){
return list1;
}
if(list1.val <= list2.val){
list1.next = Merge(list1.next, list2);
return list1;
}else{
list2.next = Merge(list1, list2.next);
return list2;
}
}
(5)复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
(图来自于网上)
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
if(pHead==null) return null;
RandomListNode currNode = pHead;
while(currNode!=null){
RandomListNode node = new RandomListNode(currNode.label);
node.next = currNode.next;
currNode.next = node;
currNode = node.next;
}
currNode = pHead;
while(currNode!=null){
RandomListNode node = currNode.next;
if(currNode.random!=null){
node.random = currNode.random.next;
}
currNode = node.next;
}
//拆分
RandomListNode pCloneHead = pHead.next;
RandomListNode tmp;
currNode = pHead;
while(currNode.next!=null){
tmp = currNode.next;
currNode.next =tmp.next;
currNode = tmp;
}
return pCloneHead;
}
}
(6)两个链表的第一个公共子节点
题目描述
输入两个链表,找出它们的第一个公共结点
注:两个链表中若有一个公共子节点,说明它们两个接下来的那一串也是一样的
import java.util.HashMap;
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode current1 = pHead1;// 链表1
ListNode current2 = pHead2;// 链表2
if (pHead1 == null || pHead2 == null)
return null;
int length1 = getLength(current1);
int length2 = getLength(current2);
// 两连表的长度差
// 如果链表1的长度大于链表2的长度
if (length1 >= length2) {
int len = length1 - length2;
// 先遍历链表1,遍历的长度就是两链表的长度差
while (len > 0) {
current1 = current1.next;
len--;
}
}
// 如果链表2的长度大于链表1的长度
else if (length1 < length2) {
int len = length2 - length1;
// 先遍历链表1,遍历的长度就是两链表的长度差
while (len > 0) {
current2 = current2.next;
len--;
}
}
//开始齐头并进,直到找到第一个公共结点
while(current1!=current2){
current1=current1.next;
current2=current2.next;
}
return current1;
}
// 求指定链表的长度
public static int getLength(ListNode pHead) {
int length = 0;
ListNode current = pHead;
while (current != null) {
length++;
current = current.next;
}
return length;
}
}
(7)删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
if(pHead==null||pHead.next==null) return pHead;
//新建一个节点,防止头结点要被删除
ListNode newHead=new ListNode(-1);
newHead.next=pHead;
ListNode pre=newHead;
ListNode p=pHead;
ListNode next=null;
while(p!=null && p.next!=null)
{
next=p.next;
if(p.val==next.val)//如果当前节点的值和下一个节点的值相等
{
while(next!=null&& next.val==p.val)
{//向后重复查找
next=next.next;
}
pre.next=next;//指针赋值,就相当于删除
p=next;
}
else//如果当前节点和下一个节点值不等,则向后移动一位
{
pre=p;
p=p.next;
}
}
return newHead.next;//返回头结点的下一个节点
}
}
(8)链表中环的入口节点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
/**假设x为环前面的路程(黑色路程),a为环入口到相遇点的路程(蓝色路程,假设顺时针走), c为环的长度(蓝色+橙色路程)
当快慢指针相遇的时候:
此时慢指针走的路程为Sslow = x + m * c + a
快指针走的路程为Sfast = x + n * c + a
2 Sslow = Sfast
2 * ( x + m*c + a ) = (x + n *c + a)
从而可以推导出:
x = (n - 2 * m )*c - a
= (n - 2 *m -1 )*c + c - a
即环前面的路程 = 数个环的长度(为可能为0) + c - a
什么是c - a?这是相遇点后,环后面部分的路程。(橙色路程)
所以,我们可以让一个指针从起点A开始走,让一个指针从相遇点B开始继续往后走,
2个指针速度一样,那么,当从原点的指针走到环入口点的时候(此时刚好走了x)
从相遇点开始走的那个指针也一定刚好到达环入口点。
所以2者会相遇,且恰好相遇在环的入口点。
最后,判断是否有环,且找环的算法复杂度为:
时间复杂度:O(n)
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead==null|| pHead.next==null|| pHead.next.next==null)return null;
ListNode fast=pHead.next.next;
ListNode slow=pHead.next;
/////先判断有没有环
while(fast!=slow){
if(fast.next!=null&& fast.next.next!=null){
fast=fast.next.next;
slow=slow.next;
}else{
//没有环,返回
return null;
}
}
//循环出来的话就是有环,且此时fast==slow.
fast=pHead;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return slow;
}
}
(9)孩子们的游戏(yuan圆圈中最后剩下的数)
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
约瑟夫环问题
解法1:l利用链表
import java.util.ArrayList;
public class Solution {
//约瑟夫环问题
//利用rrayList来解决
public int LastRemaining_Solution(int n, int m) {
if (m == 0 || n == 0) {
return -1;
}
ArrayList<Integer> data = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
data.add(i);
}
int pre = 0;
while (data.size()>1){
int cur = (pre+ m-1)%data.size();
data.remove(cur);
pre = cur;
}
return data.get(0);
}
}
解法2,利用数学解法
public class Solution
{
public int LastRemaining_Solution(int n, int m)
{
if(n==0||m==0)return -1;
int s=0;
for(int i=2;i<=n;i++)
{
s=(s+m)%i;
}
return s ;
}
}