左程云算法课基础04链表课程笔记(下)--回文单链表

题目三 判断一个链表是否为回文

笔试思路:放入栈中,弹出的顺序为逆序地顺序,然后与原链表依次比较不同则不为回文。利用快慢指针来判断这个链表中点然后只放入一半进入栈中。

public static class Node {
	public int data;
	public Node next;
	public Node(int data){this.value = data;}
}

//开辟n的额外数据空间
public static boolean isPalindrome1(Node head){
	Stack<Node> stack = new Stack<>();
	Node cur = head;
	while(cur != null){
		stack.push(cur);
		cur = cur.next;
	}
	boolean res = true;
	while(!stack.isEmpty()){
		if(head.value != stack.pop().value){
			res = false;
		}
		head = head.next;					
	}
	return res;
}

//运用快慢指针开辟n/2的额外空间
public static boolean isPalindrome2(Node head){
	Node cur = head;
	Node right = head;
	while(cur.next != null && right.next.next != null ){
		cur = cur.next;
		right = right.next.next;
	}
	cur = cur.next;
	Stack<Node> stack = new Stack<>();
	while(cur.next != null){
		stack.push(cur);
		cur = cur.next;
	}
	boolean res = true;
	while(!stack.isEmpty()){
		if(head.value != stack.pop().value){
			res = false;
		}
		head = head.next;
	}
	return res;
}

面试思路:用快慢指针找出中点,中点上的next断掉之后,后面的进行逆序,最后一个往前找的同时第一个往后找,看指向是否相同,不同则返回false,相同返回true。返回之前恢复链表。

public static boolean isPalindrome3(Node head){
	if(head == null || head.next == null){
		return true;
	}
	Node n1 = head;
	Node n2 = head;
	while(n1.next != null && n2.next.next != null){
			n1 = n1.next; //到了mid;
			n2 = n2.next.next;//到了end;
	}
	n2 = n1.next;//右边部分第一个
	n1.next = null;//把中间节点断开
	Node n3 =null;
	while(n2!= null){//倒序后半部分
		n3 = n2.next;
		n2.next = n1;
		n1 = n2;
		n2 = n3;
	}
	//此时n1为最后的一个节点
	n3 = n1;
	n2 = head;
	boolean res = true;
	while(n2 != null ){
		if(n1 != n2){
			res =false;
			break;
		}
		n2 = n2.next;
		n1 = n1.next;
	}
	n1 = n3.next;
	n3.next = null;
	while(n1 != null){
		n2 = n1.next;
		n1.next = n3;
		n3 = n1;
		n1 = n2; 
	}
	return res;
}

题目四 将单向链表按某值划分成左边小、中间相等、右边大的形式,保持相对次序不变

笔试方法:将链表放入链表数组中,运用数组的partition方法,将链表划分。

public static class Node{
	public int value;
	public Node next;
	public Node(int data){this.value = data;}
}
public static Node listPartition1(Node head,int pivot){
	Node cur = head;
	int i = 0;
	while(cur != null){
		i ++;
		cur = cur.next;
	}
	Node[] arrNode = new Node[i];
	i = 0;
	for(int i = 0; i < arrNode.length; i ++){
		arrNode[i] = head;
		head = head.next;
	}
	arrPartition(arrNode,0,arrNode.length-1,pivot);//重新划分链表数组
	//将链表数组中的节点重新串成链表并返回
	for(i = 1 ; i != arrNode.length; i ++{
		arrNode[i-1].next = arrNode[i];
	}
	return arrNode[0];
}
	
public staic void arrPartition(Node[] arrNode,int l,int r,int pivot){
	int less = l-1;
	int more = r + 1;
	int index = 0;
	while(index != r){
		if(arrNode[index].value < pivot){
			swap(arrNode,index++,++less);
		}else if(arrNode[inde].value > pivot){
			swap(arrNode,index,--more);	
		}else{
			index ++;
		}
	}
}

public static void swap(Node arrNode ,int i; int j){
	Node tmp = arr[i];
	arr[i] = arr[j];
	arr[j] = tmp;
}

面试方法:首先将每个节点与原链表断开,运用六个变量,sH,sT,eH,eT,bH,bT。分别表示小于、等于、大于区域的头和尾节点,从第一个开始依次判断与划分值的关系,如果小于则先判断头和尾是否空为空则此值作为头和尾,当下一个小于值到来的时候,将其作为上一个尾的连接之后将其作为新的尾。等三个区域都划分好之后,头尾相连成为新的链表即可。

在这里插入图片描述

public static class Node{
	public int value;
	public Node next;
	public Node(int data){this.value = data;}
}
public static Node listPartition2(Node head,int pivot){
	Node sH = null;//设置六个变量
	Node sT = null;
	Node eH = null;
	Node eT = null;
	Node bH = null;
	Node bT = null;
	Node next = null;//记录下一个节点
	if(head == null || head.next == null){
		return head;
	}
	while(head != null){
		next = head.next;//记录下一个节点
		head.next = null;//head与原链表断开
		if(head.value < pivot){
			if(sH == null && sT == null){
				sH = head;
				sT = head;
			}else{
				sT.next = head;//区域的尾节点指向这个新的节点
				sT = head;//这个新节点成为尾节点
			}
		}else if(head.value == pivot){
			if(eH == null && eT == null){
				eH = head;
				eT = head;
			}else{
				eT.next = head;//区域的尾节点指向这个新的节点
				eT = head;//这个新节点成为尾节点
			}
		}else{
			if(bH == null && bT == null){
				bH = head;
				bT = head;
			}else{
				bT.next = head;//区域的尾节点指向这个新的节点
				bT = head;//这个新节点成为尾节点
			}
		}
		head = next;
	}
	if(sT != null){
		sT.next = eH;
	}
	if(eT != null){
		eT.next = bH;
	}
	return sH != null ? sH:eH != null ? eH : bH;
}

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

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

class Node {
	int value;
	Node next;
	Node rand;
	Node(int val) {value = val;}
}

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

笔试思路:采用额外空间HashMap,将Key放入原来的链表Node,value中放入新的链表节点。然后遍历老链表,从HashMap中得到老链表next和rand指针的指向,使得新链表的rand和next也指向老链表的value中的对应的新链表节点,返回新链表的头即可。
在这里插入图片描述

public static Node copyList(Node head){
	HashMap<Node,Node> hashMap1 = HahsMap<>();
	Node cur = head;
	while(cur != null){
		hashMap1.put(cur,new Node(cur.value));
		cur = cur.head;
	}
	cur = head;
	while(cur! = null){
		cur.rand = hashMap1.get(cur).rand;
		cur.next = hashMap1.get(cur).next;
		cur = cur.next;
	}
	return hashMap1.get(cur);
}

面试思路:不采用HashMap方法,生成克隆节点,将其连在老链表节点的后面,其中克隆节点的rand指针就是采用,原节点指向的节点的next就可以找到克隆节点的rand指针所指向的节点。

public static Node copyList2(Node head){
	Node cur = head;
	Node nextOne =null;
	//将克隆节点复制值并放在原节点之后
	while(cur != null){
		next = cur.next;
		cur.next = new Node(cur.value);//其后为新建的克隆节点
		cur.next.next = nextOne;//克隆节点连接原节点的下一个
		cur  = nextOne;//继续往后
	}
	cur = head;
	while(cur != null){
		nextOne = cur.next.next;
		Node copyNode = cur.next;
		copyNode.rand = cur.rand != null ? cur.rand.next : null;//克隆节点的rand指向原节点rand指针指向节点的next
		cur = nextOne;
	}
	cur = head;
	Node res = head;
	//拆开
	while(cur != null){
		nextOne = cur.next.next;
		Node copyNode = cur.next;
		cur.next = nextOne;
		copyNode.next = nextOne != null ? nextOne.next : null;
		cur = nextOne;
	}
	return res;
}

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

【题目】给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null
【要求】如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)

首先要判断是否有环,写一个函数返回第一个入环的节点`
思路1:利用HashSet装入链表,遍历原链表的同时从HashSet中寻找,如果存在则有环,否则无环。

public static Node isCircle(Node head){
        HashSet<Node> hashSet1 = new HashSet<>();
        Node cur = head;

        while(cur != null){
            if(hashSet1.contains(cur)){
                return cur;
            }
            hashSet1.add(cur);
            cur = cur.next;

        }
        return null;
    }

思路2:利用快慢指针,分别一次走两步和一步,其中一个到了null则无环,若两个相遇则有环。相遇的时候让快指针返回开头,两个指针同时走一步一次,他们再次相遇则就为入环的第一个节点(定理)

public static Node isCircle2(Node head){
        Node n1 = head.next;
        Node n2 = head.next.next;
        while (n1 != n2){
            if (n1.next == null || n2.next.next == null){
                return null;
            }
            n1 = n1.next;
            n2 = n2.next.next;
        }
        n2 = head;
        while (n1 != n2){
            n1 = n1.next;
            n2 = n2.next;
        }
        return n1;
    }

看两个无环单链表是否相交,首先统计两个的长度和结尾节点,首先判断结尾是否相同,不同则不相交,如相同则让长的先走差值步,之后再一起走,两者相等则是第一个相交的节点处

public static Node bothNoCircle(Node head1,Node head2){
        Node cur1 = head1;
        Node cur2 = head2;
        int i = 0;//用i来计算两个链表的差值
        while (cur1.next != null){//因为后面需要比较cur1和cur2是否相同,所以让next不为null
            i ++;
            cur1 = cur1.next;
        }
        while (cur2.next != null){
            i --;
            cur2 = cur2.next;
        }
        if (cur1 != cur2){//判断结尾是否一样,不同则不相交
            return null;
        }
        //这时让长的为cur1,短的为cur2;
        cur1 = i > 0 ? head1 : head2;
        cur2 = cur1 == head1 ? head2 : head1;
        i = Math.abs(i);
        while (i != 0){
            cur1 = cur1.next;
            i --;
        }
        while (cur1 != cur2){
            cur1 = cur1.next;
            cur2= cur2.next;
        }
        return cur1;
    }

一个有环一个无环不可能相交
两个链表都有环,有以下三种情况,首先第二种情况把入环点作为终止节点,运用之前两个无环链表相交的方法找出相交的第一个节点
如何区分一和三呢,采用先找到一个入环点,使其往下走,如果在遇到自己之前和另一个链表的入环点遇上则相交。

在这里插入图片描述

public static Node bothCircle(Node head1 , Node head2) {
        Node loop1 = isCircle1(head1);
        Node loop2 = isCircle1(head2);
        Node cur1 = head1;
        Node cur2 = head2;
        if (loop1 == loop2) {
            int i = 0;
            while (cur1 != loop1) {
                i++;
                cur1 = cur1.next;
            }
            while (cur2 != loop2) {
                i--;
                cur2 = cur2.next;
            }
            cur1 = i > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            i = Math.abs(i);
            while (i != 0) {
                cur1 = cur1.next;
                i--;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        }else {
            cur1 = loop1.next;
            while (cur1 != loop1){
                if (cur1 == loop2){
                    return cur1;
                }
                cur1 = cur1.next;
            }
            return  null;
        }
    }

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值