链表相关问题

                1.求链表中倒数第k个结点

                   分析:可以使用两个指针pre和post,同时指向正向数第一个结点,post指针先走,走到正数第k+1个(即是的post的节点序号-pre的节点序号=k),然后pre和post一起走,直到post走到链表结尾。

                   代码

		Node pre = list.head;
		Node post = pre;
		int count = 0;
		
		while(count<k && post!=null)
		{
			post = post.next;
			count++;
		}
		
		while(post!=null)
		{
			post = post.next;
			pre = pre.next;
		}
		System.out.println(pre.value);

                2.求链表中的中间节点

                    分析:使用两个指针pre和post,同时指向正向数第一个结点。pre和post指针同时走,不过post一次走两步,pre一次走一步,直至post走到链表结尾。

                    代码

	Node pre = list.head;
		Node post = pre;
		
		while(post!=null)
		{
			post = post.next;
			if(post==null)
				break;
			else
				post = post.next;
			pre = pre.next;
		}
		System.out.println(pre.value);

                 由此,可以推广到求链表的第1/3、1/4.....个结点

       3.判断链表中是否有环

                   分析:如果链表中有环,那么遍历链表就会无限循环下去。使用两个指针p、q,p一次走两步,q一次走一步,如果有环,那么p、q总会相遇

                   代码

		Node p = list1.head;
		Node q = p;
		
		boolean hasCircle = false;
		
		while(p!=null)
		{
			
			p = p.next;
			if(p==null)
				break;
			p = p.next;
			
			q = q.next;
				
			if(p==q)
			{
				hasCircle = true;
				break;
			}
		}

		System.out.println(hasCircle);

        3.判断两个链表中是否有公共结点

                   分析:如果两个链表中有公共结点,那么遍历链表必然呈“Y型”,而不可能是“X”型。最差情况下是最后一个结点为公共结点,所以第一次先遍历第一个链表,保存最后一个结点,再遍历第二个链表,保存最后一个结点。最后比较两个链表的最后一个结点是否相同,相同就有公共结点,否则没有

                   代码

	Node p = list1.head;
		Node q = list2.head;
		
		while(p.next!=null)
		{
			p = p.next;
		}
		
		while(q.next!=null)
		{
			q = q.next;
		}
		
		if(q != p)
			System.out.println("没有相交的点");


        4.求两个链表中第一个公共结点

                   分析:如果两个链表中有公共结点,那么两个链表分开看必然是结尾对齐的,所以,如果两个链表长度不同,以结尾对齐,长的链表多出来的那部分必然不可能是公共结点,所以先将长链表的指针移动到长度相同的位置,然后再分别比较两个链表的结点

                   代码

	Node p = list1.head;
		Node q = list2.head;
		
		while(p.next!=null)
		{
			p = p.next;
		}
		
		while(q.next!=null)
		{
			q = q.next;
		}
		
		if(q != p)
			System.out.println("没有相交的点");
		
		int len1 = list1.length();
		int len2 = list2.length();
		
		int count = len1>len2?len1-len2:len2-len1;
		p = len1>len2?list1.head:list2.head;
		q = p==list1.head?list2.head:list1.head;
		while(count>0)
		{
			p = p.next;
			count--;
		}
		
		while(p!=null)
		{
			if(p==q)
				break;
			else
			{
				p = p.next;
				q = q.next;
			}
		}
		
		//p就为公共结点
		System.out.println(p.value);


        5.含环链表中入环第一个结点

                   分析:先判断是否有环,如果有环,则通过环中的一个结点(通常为判断出有环的那个结点)处断开,那么这个点和链表起始结点就形成了两个链表,则问题就转化为求两个链表的第一个公共结点的问题

                   代码:综合前面代码即可



        6.给出链表头结点和p,删除p指向的结点,要求时间复杂度为O(1)

                   分析:通常情况下都是通过p的前一个结点指向p的下一个结点来删除,但是要求时间复杂度为O(1),所以,可以使用后一个结点的值赋给p指向的结点,再删除p的下一个结点即可

                   代码:

		p.value = p.next.value;
		p.next = p.next.next;


        7.从尾到头打印链表中数据

                   分析:对于这种顺序颠倒的问题,就应该使用栈或者递归

                   代码:

		Node p = list1.head;
		
		Stack<Integer> stack = new Stack<Integer>();
		
		while(p!=null)
		{
			stack.push(p.value);
			p = p.next;
		}
		
		while(!stack.isEmpty())
			System.out.print(stack.pop()+" ");
                   使用递归
	private static void reverse(Node p)
	{
		if(p==null)
			return;
		
		reverse(p.next);
		System.out.print(p.value+" ");
	}

        8.链表反转

                   分析:从第二个结点开始,将结点直接变为头指针指向的对象(不含头结点的链表)

                   代码:

		Node p = list1.head;
		Node first = p;
		Node q = list2.head;
		
		if(p.next==null)
			return;
		
		p = p.next;
		while(p!=null)
		{
			Node cur = p;
			p = p.next;
			
			cur.next = list1.head;
			list1.head = cur;
		}
		first.next = null;


        9.链表中环的长度

                   分析:先判断是否有环,如果有环,则从判断出有环的那个节点处开始,只移动一个指针一次一个节点,下一次两个指针再相同时就是环的长度了


        10.O(1)时间复杂度删除链表中指定节点p

                   分析:一般情况下均为先获得p的前驱节点q,然后q->next = p->next。这里因为任意节点结构都是相同的,不同的只是其中的数值,所以可以将p后继节点的值赋值给p,然后删除后继节点即可

                   代码:

		Node nextNode = p.next;
		//p指向的不是最后一个节点
		if(nextNode != null)
		{
			p.value = nextNode.value;
			p.next = nextNode.next;
		}
		//p指向最后一个节点
		else
			p.next = nextNode.next;

        11.调整数组,使得所有奇数排在所有偶数之前(数组顺序可以打乱)

                   分析:使用两个指针,分别指向数组的开始和结尾,类似快排的调整,直至两个指针相遇

                   代码:

		int[] a = {1,2,3,4,5};
		int begin = 0;
		int end = a.length-1;
		
		for(int x:a)
			System.out.print(x+" ");
		System.out.println();
		while(begin<end)
		{
			while(a[begin]%2!=0 && begin<end)
				begin++;
			while(a[end]%2==0 && begin<end)
				end--;
			if(a[begin]%2==0 && begin<end)
			{
				int temp = a[begin];
				a[begin] = a[end];
				a[end] = temp;
			}
		}


        12.已知集合A和B的元素分别用不含头结点的单链表存储,求A-B,并将结果保存在A中

                   例子:集合A={5,10,20,15,25,30},集合B={5,15,35,25},完成后A={10,20,30}                   

                   分析:即求在A且不在B中的元素,那么就循环B中所有元素,每个都在A中查找,如果找到则在A中删除即可。(这里使用O(1)删除链表的方法,除了最后一个节点,其余的均将后面节点的值幅值过来即可)

                   代码:

                   定义节点类型

	private class Node
	{
		private int num;
		private Node next;
		
		public Node(int num) 
		{
			super();
			this.num = num;
			this.next = null;
		}
		
	}
                     构造链表并求差集

		Node na1 = new Node(5);
		Node na2 = new Node(10);
		Node na3 = new Node(20);
		Node na4 = new Node(15);
		Node na5 = new Node(25);
		Node na6 = new Node(30);
		
		na1.next = na2;
		na2.next = na3;
		na3.next = na4;
		na4.next = na5;
		na5.next = na6;
		
		Node nb1 = new Node(5);
		Node nb2 = new Node(15);
		Node nb3 = new Node(35);
		Node nb4 = new Node(25);
		
		nb1.next = nb2;
		nb2.next = nb3;
		nb3.next = nb4;
		
		
		
		for(Node pb=nb1; pb!=null; pb=pb.next)
		{
			boolean isEnd = false;//用来判断是不是A中最后一个元素
			for(Node pa = na1; pa!=null; pa=pa.next)
			{
				if(pa.num == pb.num)
				{
					if(pa.next != null)//如果不是最后一个元素,那么直接O(1)删除
					{
						pa.num = pa.next.num;
						pa.next = pa.next.next;
					}
					else
						isEnd = true;//是A最后一个元素
					
					break;
				}
				
			}
			if(isEnd)//是A最后一个元素
			{
				Node pre = na1;
				for(Node pa = na1; pa.next!=null; pa=pa.next)
				{
					pre = pa;
				}
				pre.next = null;
			}
			
		}
		Node p = na1;
		
		while(p!=null)
		{
			System.out.print(p.num+" ");
			p = p.next;
		}
	
                         结果为:

10 20 30 



        13.合并两个非递增链表       

                   分析:首先定义新链表头指针,先确定头指针指向谁,然后使用一个cur指针,指向新链表的当前节点,依次更新即可

                   代码:

	@Test
	public void test() 
	{
		LinkNode head1 = null;
		LinkNode node1 = new LinkNode(1);
		LinkNode node2 = new LinkNode(3);
		LinkNode node3 = new LinkNode(5);
		LinkNode node4 = new LinkNode(7);
		LinkNode node5 = new LinkNode(9);
		node1.next = node2;
		node2.next = node3;
		node3.next = node4;
		node4.next = node5;
		head1 = node1;
		
		LinkNode head2 = null;
		LinkNode node6 = new LinkNode(2);
		LinkNode node7 = new LinkNode(4);
		LinkNode node8 = new LinkNode(6);
		node6.next = node7;
		node7.next = node8;
		head2 = node6;
		
		LinkNode newHead = null;   //新链表的头结点
		LinkNode cur = null;       //新链表的当前节点
		
		LinkNode pa = head1;       //pa指向链表1的当前节点
		LinkNode pb = head2;       //pb指向链表2的当前节点
		
		if(pa.val < pb.val)       //首先先确定新链表头结点的指向
		{
			newHead = pa;
			cur = pa;
			pa = pa.next;    
		}
		else
		{
			newHead = pb;
			cur = pb;
			pb = pb.next;
		}
		
		while(pa!=null && pb!=null)    //依次递归,选择小的加入新链表
		{
			if(pa.val < pb.val) 
			{
				cur.next = pa;
				cur = pa;
				pa = pa.next;
			}
			else
			{
				cur.next = pb;
				cur = pb;
				pb = pb.next;
			}
		}
		
		if(pa!=null)
			cur.next = pa;
		if(pb!=null)
			cur.next = pb;
		
		while(newHead!=null)
		{
			System.out.print(newHead.val+" ");
			newHead = newHead.next;
		}
		
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值