链表解决常见的几种问题


自定义的Linked类

package com.amanda.demo03;

/*
 * 	Linked:单向链表实现类
 */
public class Linked {

	Node first; // 头结点
	Node last; // 尾节点
	int size; // 链表长度
	
    // Node链表中的节点

	static class Node {
		int val; // 数据
		Node next; // 下一个元素

		public Node(int x) {
			val = x;
		}
	}

	// 添加链表元素(尾插法)
	public void add(int val) {
		// 获取链表的尾节点
		final Node l = last;
		// 创建新节点
		final Node newNode = new Node(val);

		// 判断原来的尾节点是否等于null
		if (l != null) {
			l.next = newNode; // 让原来的尾节点的next -> 新节点
		} else {
			first = newNode; // 首节点 -> 新节点
		}
		last = newNode; // 尾节点 -> 新节点
		size++; // 链表长度递增
	}

	/*
	 * 获取链表长度
	 */
	public int size() {
		int size = 0;
		for (Node x = first; x != null; x = x.next) {
			size++;
		}
		return size;
	}

	/*
	 * 遍历链表所有节点
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		for (Node x = first; x != null; x = x.next) {

			if (x.next != null) {
				sb.append(x.val + "->");
			} else {
				sb.append(x.val);
			}

		}
		return sb.toString();
	}
}

一、反转链表

思路:先将链表中的元素存储至栈中,借助栈的特性先进后出,再将栈中的元素出栈实现链表的反转

实现过程如下:

public static void main(String[] args) {	
		Linked node = new Linked();
		node.add(1);
		node.add(2);
		node.add(3);		
		System.out.println("顺序链表:"+node);	
		Linked link = reverse(node);
		System.out.println("逆序链表:"+link);
	}

	public static Linked reverse(Linked link) {

		// 用于存储链表中的Node对象
		Stack<Linked.Node> stackNode = new Stack<Linked.Node>();
		// 用于保存出栈的Node节点链表
		Linked result = new Linked();
		// 获取当前链表的头节点
		Node currentNode = link.first;

		// 如果头节点为null,则返回null
		if (currentNode == null) {
			return null;
		}

		// 否则将链表中的Node对象存储至栈中,并向下遍历
		while (currentNode != null) {
			stackNode.add(currentNode);
			currentNode=currentNode.next;
		}
		
		// 出栈,将Node对象保存至新链表中
		while(!stackNode.isEmpty()) {
			result.add(stackNode.pop().val);
		}
		return result;
	}

二、计算两数之和

思路:使用链表计算两数之和,打破了数据类型的有限性。由于使用单项列表,只能从头开始遍历所以头节点保存的是数字的个位,依次类推

实现过程如下:

	public static void main(String[] args) {
		Linked link1 = new Linked();
		link1.add(2);
		link1.add(4);
		link1.add(7);
		
		Linked link2 = new Linked();
		link2.add(5);
		link2.add(6);
		link2.add(3);
		
		Linked link3 = addTwoNumbers(link1, link2);
		System.out.println("数字1:"+link1);
		System.out.println("数字2:"+link2);
		System.out.println("两数和:"+link3);
	}

	public static Linked addTwoNumbers(Linked link1, Linked link2) {

		// 用于保存两束之和的链表
		Linked resultLink = new Linked();
		// 获取两个链表的头节点
		Node n1 = link1.first;
		Node n2 = link2.first;
		// 用于保存进位值
		int carry = 0;

		// 循环相加
		while (n1 != null || n2 != null) {
			// 获取两个节点的值,为null则让其为0
			int x = n1 != null ? n1.val : 0;
			int y = n2 != null ? n2.val : 0;
			// 计算两数+进位值之和
			int sum = x + y + carry;
			// 将余数存储值链表
			resultLink.add(sum % 10);
			// 获取进位数
			carry = sum / 10;
			// 判断n1,n2是否为null
			if (n1 != null) {
				n1 = n1.next;
			}
			if (n2 != null) {
				n2 = n2.next;
			}
		}

		// 判断两数最高位是否产生进位数
		if (carry != 0) {
			resultLink.add(carry);
		}
		return resultLink;
	}

三、检查链表是否有环

思路:有两种做法,①采用Set集合的contains()方法;②采用快慢指针

Set集合

public static boolean hasCycle1(Node node) {
		// 先判断链表是否为空链表
		if (node == null) {
			return false;
		}
		// 用于保存Node对象
		HashSet<Node> set = new HashSet<Node>();
		while (node != null) {
			// 判断set集合是否包含Node节点
			if (set.contains(node)) {
				// 产生环
				return true;
			} else {
				// 无环
				set.add(node);
				node = node.next;
			}
		}
		return false;
	}

快慢指针

public static boolean hasCycle2(Node head) {
		// 判断头节点是否存在
		if (head == null) {
			return false;
		}
		// 获取头节点
		Node fast = head;
		Node slow = head;
		while (fast != null && fast.next != null && slow != null) {
			// 快指针每次走两步
			fast = fast.next.next;
			// 慢指针每次走一步
			slow = slow.next;
			// 判断两指针是否会相遇
			if (fast == slow) {
				return true;
			}
		}
		return false;
	}

四、检查链表是否相交

思路:①采用双重循环;②采用双指针

双重循环:采用双重遍历一一比较

public static boolean isIntersect1(Linked link1, Linked link2) {
		// 双重循环
		for (Node p = link1.first; p != null; p = p.next) {
			for (Node q = link2.first; q != null; q = q.next) {
				if (q == p) {
					return true;
				}
			}
		}
		return false;
	}

双指针:首先保证两条链表的长度一致,所以在链表长度不一致的情况下,让长的链表先走,直至与短链表的长度一致

public static boolean isIntersect2(Linked link1, Linked link2) {
		// 判断连个链表不为空链表
		if (link1 == null || link2 == null) {
			return false;
		}
		// 获取长链表的头节点
		Node p = link1.size() > link2.size() ? link1.first : link2.first;
		// 获取短链表的头节点
		Node q = link1.size() > link2.size() ? link2.first : link2.first;
		// 计算两个链表长度差值
		int diff = Math.abs(link1.size() - link2.size());
		// 让长链表先移动,使其两个链表的长度相等
		while (diff-- > 0) {
			p = p.next;
		}
		// q和p同时移动
		while (q != p) {
			q = q.next;
			p = p.next;
		}
		// 跳出循环的两种可能
		// 1:链表遍历完毕
		// 2:q和p相等
		if (q != null) {
			return true;
		} else {
			return false;
		}
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MM呦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值