链表 java 实现

单链表

public class SingleLinkedListDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 开始测试
		HeroNode hero1 = new HeroNode(1, "亚索", "疾风剑豪");
		HeroNode hero2 = new HeroNode(2, "易", "无极剑圣");
		HeroNode hero3 = new HeroNode(3, "菲奥娜", "无双剑姬");
		HeroNode hero4 = new HeroNode(4, "阿托", "暗裔剑魔");
		// 创建单链表
		SingleLinkedList list = new SingleLinkedList();
//		list.add(hero1);
//		list.add(hero2);
//		list.add(hero3);
//		list.add(hero4);
//		System.out.println("普通 插入……");
//		list.list();
		// 按顺序添加
		list.addByNo(hero2);
		list.addByNo(hero4);
		list.addByNo(hero3);
		list.addByNo(hero2);
		list.addByNo(hero1);
		// 按排序插入完打印下
		System.out.println("orderbyno 插入……");
		list.list();
		// 测试修改
		HeroNode hero5 = new HeroNode(4, "亚托克斯", "光明使者");
		list.update(hero5);
		System.out.println("修改完的链表");
		list.list();
		// 测试删除
		list.del(2);
		list.del(4);
		System.out.println("删除完的链表");
		list.list();
		// 测试链表中的有效个数
		System.out.println(list.getLength(list.getHead()));
		// 测试是否查找到单链表中倒数第k个节点
		System.out.println(list.getLastIndexNode(list.getHead(),4));
		// 测试单链表的反转
		list.reverse(list.getHead());
		System.out.println("反转后的链表");
		list.list();
		// 测试从尾到头打印单链表
		System.out.println("从尾到头……");
		list.reverseList(list.getHead());
		// 测试合并两个有序链表
		SingleLinkedList list2 = new SingleLinkedList();
		list2.addByNo(hero2);
		list2.addByNo(hero4);
		list.reverse(list.getHead());
		System.out.println("打印list1");
		list.list();
		System.out.println("打印list2");
		list2.list();
		System.out.println("打印合并后的链表");
		System.out.println(list.mergeTwoList(list.getHead(), list2.getHead()));
	}

}
// 创建单链表但列表 管理英雄节点
class SingleLinkedList{
	// 先定义一个头结点 头结点不要动 不存放具体的数据
	HeroNode head = new HeroNode(0, "", "");
	
	// 添加节点到单链表
	// 思路:不考虑英雄排名顺序时 
	// 1、先找到当前链表的最后节点
	// 2、将最后节点的next指向新的节点
	public void add(HeroNode heroNode) {
		// 定义一个临时节点 指向head;
		HeroNode temp = head;
		while (true) {
			// 找到链表的最后一个节点位置
			if (temp.next == null) {
				break;
			}
			// 如果没有找到最后 将指针后移
			temp = temp.next;
		}
		// 退出循环时即找到了当前链表的最后一个节点
		// 将当前节点的next 指向新的节点
		temp.next = heroNode;
	}
	
	
	// 添加节点到单链表
	// 思路:考虑英雄排名顺序时 
	// 1、先找到要插入节点的前一个位置
	// 2、heroNode.next = temp.next
	// 3、temp.next = heroNode
	public void addByNo(HeroNode heroNode) {
		// 定义一个临时节点 指向head;
		HeroNode temp = head;
		boolean flag = false;// 标志要插入的节点是否已经存在 默认不存在
		while (true) {
			// 找到链表的最后一个节点位置
			if (temp.next == null) {
				break;
			}
			if (temp.next.no > heroNode.no) {// 找到要插入节点的前一个位置
				break;
			}else if (temp.next.no == heroNode.no) {
				// 要插入的节点已经存在
				flag = true;
				break;
			}
			// 如果没有找到 将指针后移
			temp = temp.next;
		}
		if (!flag) {
			// 即找到了要插入节点的前一个位置
			heroNode.next = temp.next;
			temp.next = heroNode;
		}else {
			// 说明要插入的节点已经存在
			System.out.printf("要插入的节点 %d 已存在,不能插入\n",heroNode.no);
		}
	}
	
	// 修改节点
	// 思路:先判断找没找到要修改的节点
	// 找到了让heroNode的name 和 nicename 赋值给当前节点
	// 没找到就显示没有要修改的节点 无法修改
	public void update(HeroNode heroNode) {
		// 头结点不能动所以需要临时节点
		HeroNode temp = head;
		boolean flag = false;// 标志找没找到要修改的节点
		while (true) {
			// 链表空了
			if (temp.next == null) {
				break;
			}
			if (temp.next.no == heroNode.no) {
				// 找到要修改的节点
				flag = true;
				break;
			}
			// 指针后移 遍历链表
			temp = temp.next;
		}
		if (flag) {
			// 找到了要修改的节点 开始修改
			temp.next.name = heroNode.name;
			temp.next.nickname = heroNode.nickname;
		}else {
			System.out.printf("要添加的节点%d不存在,无法修改\n", heroNode.no);
		}
	}
	
	// 删除链表
	// 思路:先找到要删除链表的前一个位置 将temp.next = temp.next.next即删除
	public void del(int no) {
		// 老规矩 头结点不能动 
		HeroNode temp = head;
		boolean flag = false;// 标志找没找到要删除的节点
		while (true) {
			// 链表空了
			if (temp.next == null) {
				break;
			}
			if (temp.next.no == no) {
				// 找到要删除的节点了
				flag = true;
				break;
			}
			// 没找到就指针后移 遍历链表
			temp  = temp.next;
		}
		if (flag) {
			// 找到了要删除的节点
			temp.next = temp.next.next;
		}else {
			System.out.printf("要删除的节点%d不存在,无法删除", no);
		}
	}
	
	// 打印链表
	public void list() {
		// 先判断当前链表是否为空
		if (head == null) {
			System.out.println("当前链表为空,没有数据呐,兄弟");
			return;
		}
		// 定义一个临时变量辅助遍历
		HeroNode temp = head.next;
		while (true) {
			// 判断是否到了最后
			if(temp == null) {
				break;
			}
			// 打印节点内容
			System.out.println(temp.toString());
			// 后移指针
			temp = temp.next;					
		}
	}
	
	/**
	 * 获取单链表中的有效个数
	 * @param head 传进来的头结点
	 * @return 链表中的有效个数
	 */
	public int getLength(HeroNode head) {
		//空链表
		if (head == null || head.next == null) {
			return 0;
		}
		// 头结点不计算
		HeroNode cur = head.next;
		int count = 0;
		while(cur != null) {
			count++;// 后移
			cur = cur.next;
		}
		return count;
	}

	// 查找单链表中倒数第k个节点
	public HeroNode getLastIndexNode(HeroNode head,int index) {
		//空链表
		if (head == null || head.next == null) {
			return null;
		}
		
		HeroNode cur = head.next;
		// 获得当前链表的有效个数
		int count = getLength(head);
		// 倒数index不存在
		if (index < 0 || index > count) {
			return null;
		}
		// 返回count - index 的节点
		// count = 3 index = 1
		for (int i = 0; i < count - index; i++) {
			cur = cur.next;
		}
		return cur;
	}

	// 单链表的翻转
	public void reverse(HeroNode head) {
		// 链表为空或只有一个数据 不需要翻转 直接返回
		if (head.next == null || head.next.next == null) {
			return;
		}
		// 定义一个新链表 
		HeroNode newHead = new HeroNode(0, "", "");
		// 辅助变量 帮助我们遍历
		HeroNode cur = head.next;
		// 定义一个next 指向当前cur的下一个节点
		HeroNode next = null;
		// 遍历当前链表 将遍历的节点取出 存放在newHead的最前面
		while(cur != null) {
			next = cur.next;// 存放当前节点的下一个节点
			cur.next = newHead.next;// 将cur指向newHead的下一个节点 
			newHead.next = cur;// 将newHead的下一个节点指向cur 即遍历当前链表取出节点放在newHead的最前面
			cur = next;// 后移
		}
		// 最后将head 指向newHead.next并返回  完成翻转
		head.next = newHead.next;
	}
	
	// 从尾到头打印单链表
	public void reverseList(HeroNode head) {
		if (head == null || head.next == null) {
			System.out.println("当前链表为空");
			return;
		}
		// 使用栈
		Stack<HeroNode> stack = new Stack<HeroNode>();
		// 使用辅助变量 帮助遍历
		HeroNode cur = head.next;
		while(cur != null) {
			stack.push(cur);// 遍历存入栈中
			cur = cur.next;// 后移
		}
		// 开始出栈
		while (stack.size() > 0) {
			System.out.println(stack.pop());
		}
		
	}
	
	// 合并两个有序链表
	public HeroNode mergeTwoList(HeroNode node1,HeroNode node2) {
		// 两个链表为空
		if (node1.next == null && node2.next == null) {
			return null;
		}
		// 定义一个新链表 
		HeroNode newHead = new HeroNode(0, "", "");
		HeroNode prev = newHead;
		// 遍历当前链表 将遍历的节点取出 存放在newHead的最前面
		while (node1.next != null && node2.next != null) {
			if (node1.next.no <= node2.next.no) {
				prev.next = node1.next;
				node1.next = node1.next.next;
			}else {
				prev.next = node2.next;
				node2.next = node2.next.next;
			}
			prev = prev.next;
		}
		prev.next = node1.next == null?node2.next:node1.next;
		return newHead.next;
	}
	
	public HeroNode getHead() {
		return head;
	}


	public void setHead(HeroNode head) {
		this.head = head;
	}
	
	
}
// 先创建一个英雄节点
class HeroNode{
	public int no;// 英雄编号
	public String name;// 英雄名称
	public String nickname;// 英雄昵称
	
	public HeroNode next;
	
	// 有参构造方法 alt + shift + s + o
	public HeroNode(int no, String nameString, String nicknameString) {
		this.no = no;
		this.name = nameString;
		this.nickname = nicknameString;
	}
	// tostring()
	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", nameString=" + name + ", nicknameString=" + nickname + next+"]";
	}
	
	
}

双链表

public class DoubleLinkedListDemo {
	public static void main(String[] args) {
		// 开始测试
		HeroNode2 hero1 = new HeroNode2(1, "亚索", "疾风剑豪");
		HeroNode2 hero2 = new HeroNode2(2, "易", "无极剑圣");
		HeroNode2 hero3 = new HeroNode2(4, "菲奥娜", "无双剑姬");
		HeroNode2 hero4 = new HeroNode2(5, "阿托", "暗裔剑魔");
		// 创建双向链表
		DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
		doubleLinkedList.add(hero1);
		doubleLinkedList.add(hero2);
		doubleLinkedList.add(hero3);
		doubleLinkedList.add(hero4);
		
//		// 打印双向链表
//		doubleLinkedList.list();
		
		// 测试按排名添加节点
//		doubleLinkedList.addByNo(hero4);
//		doubleLinkedList.addByNo(hero2);
//		doubleLinkedList.addByNo(hero3);
//		doubleLinkedList.addByNo(hero1);
		// 打印双向链表
		
		doubleLinkedList.list();
		HeroNode2 hero5 = new HeroNode2(4, "亚托克斯", "光明使者");
		// 测试修改
		doubleLinkedList.update(hero5);
		System.out.println("update ");
		doubleLinkedList.list();
		// 测试删除
		doubleLinkedList.del(3);
		System.out.println("del");
		doubleLinkedList.list();
		HeroNode2 hero6 = new HeroNode2(3, "小剑魔", "使者");
		System.out.println("by order print");
		doubleLinkedList.addByNo(hero6);
		doubleLinkedList.list();
	}
}
// 双向链表
class DoubleLinkedList{
	HeroNode2 head = new HeroNode2(0,"", "");
	
	public HeroNode2 getHead() {
		return head;
	}
	
	// 添加节点到单链表
	// 思路:不考虑英雄排名顺序时 
	// 1、先找到当前链表的最后节点
	// 2、将最后节点的next指向新的节点
	public void add(HeroNode2 heroNode) {
		// 定义一个临时节点 指向head;
		HeroNode2 temp = head;
		while (true) {
			// 找到链表的最后一个节点位置
			if (temp.next == null) {
				break;
			}
			// 如果没有找到最后 将指针后移
			temp = temp.next;
		}
		// 退出循环时即找到了当前链表的最后一个节点
		temp.next = heroNode;
		heroNode.pre = temp;
	}
	
	
	// 添加节点到单链表
	// 思路:考虑英雄排名顺序时 
	// 1、先找到要插入节点的前一个位置
	// 2、heroNode.next = temp.next
	// 3、temp.next = heroNode
	public void addByNo(HeroNode2 heroNode) {
		// 定义一个临时节点 指向head;
		HeroNode2 temp = head;
		boolean flag = false;// 标志要插入的节点是否已经存在 默认不存在
		while (true) {
			// 找到链表的最后一个节点位置
			if (temp.next == null) {
				break;
			}
			if (temp.no > heroNode.no) {// 找到要插入节点的前一个位置
				break;
			}else if (temp.no == heroNode.no) {
				// 要插入的节点已经存在
				flag = true;
				break;
			}
			// 如果没有找到 将指针后移
			temp = temp.next;
		}
		if (!flag) {
			// 即找到了要插入节点的前一个位置
			temp.pre.next = heroNode;
			heroNode.next = temp;
			heroNode.pre = temp.pre;
			temp.pre = heroNode;
		}else {
			// 说明要插入的节点已经存在
			System.out.printf("要插入的节点 %d 已存在,不能插入\n",heroNode.no);
		}
	}
	
	// 修改节点
	// 思路:先判断找没找到要修改的节点
	// 找到了让heroNode的name 和 nicename 赋值给当前节点
	// 没找到就显示没有要修改的节点 无法修改
	public void update(HeroNode2 heroNode) {
		// 头结点不能动所以需要临时节点
		HeroNode2 temp = head;
		boolean flag = false;// 标志找没找到要修改的节点
		while (true) {
			// 链表空了
			if (temp == null) {
				break;
			}
			if (temp.no == heroNode.no) {
				// 找到要修改的节点
				flag = true;
				break;
			}
			
			// 指针后移 遍历链表
			temp = temp.next;
		}
		if (flag) {
			// 找到了要修改的节点 开始修改
			temp.name = heroNode.name;
			temp.nickname = heroNode.nickname;
		}else {
			System.out.printf("要修改的节点%d不存在,无法修改\n", heroNode.no);
		}
	}
	
	// 删除链表
	// 思路:先找到要删除链表的前一个位置 将temp.next = temp.next.next即删除
	public void del(int no) {
		if (head.next == null) {
			System.out.println("链表为空无法删除");
			return;
		}
		// 老规矩 头结点不能动 
		HeroNode2 temp = head;
		boolean flag = false;// 标志找没找到要删除的节点
		while (true) {
			// 链表空了
			if (temp == null) {
				break;
			}
			if (temp.no == no) {
				// 找到要删除的节点了
				flag = true;
				break;
			}
			// 没找到就指针后移 遍历链表
			temp  = temp.next;
		}
		if (flag) {
			// 找到了要删除的节点
			temp.pre.next = temp.next;
			if (temp.next != null) {
				// 判断temp是否为链表尾部
				temp.next.pre = temp.pre;
			}else {
				// 当前temp在链表尾部
				temp.pre = null;
			}
		}else {
			System.out.printf("要删除的节点%d不存在,无法删除", no);
		}
	}
	
	// 打印链表
	public void list() {
		// 先判断当前链表是否为空
		if (head == null) {
			System.out.println("当前链表为空,没有数据呐,兄弟");
			return;
		}
		// 定义一个临时变量辅助遍历
		HeroNode2 temp = head.next;
		while (true) {
			// 判断是否到了最后
			if(temp == null) {
				break;
			}
			// 打印节点内容
			System.out.println(temp.toString());
			// 后移指针
			temp = temp.next;					
		}
	}
}
//先创建一个英雄节点
class HeroNode2{
	public int no;// 英雄编号
	public String name;// 英雄名称
	public String nickname;// 英雄昵称
	public HeroNode2 pre;// 前驱
	public HeroNode2 next;// 后继
	
	// 有参构造方法 alt + shift + s + o
	public HeroNode2(int no, String nameString, String nicknameString) {
		this.no = no;
		this.name = nameString;
		this.nickname = nicknameString;
	}
	// tostring()
	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", nameString=" + name + ", nicknameString=" + nickname +"]";
	}
	
}

链表实际应用——约瑟夫环

// 吃小孩的约瑟夫问题
public class Josephu {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 测试一下
		CircleSingleLinkedList cList = new CircleSingleLinkedList();
		cList.add(250);// 创建5个超可爱的小孩
		// 打印他们
		cList.list();
		cList.getBoy(250, 1, 2);
	}

}
// 单向环形链表
class CircleSingleLinkedList{
	// 创建一个first结点 当前没有编号
	Boy first = null;
	
	// 添加小孩结点 创建环形链表
	public void add(int nums) {
		// nums做一个数据校验
		if (nums < 1) {
			System.out.println("你输入的小孩个数太少了 不够吃的啊");
			return;
		}
		Boy curBoy = null;// 辅助指针,帮助构建环形链表
		// 使用循环来建立环形链表
		for (int i = 1; i <= nums; i++) {
			// 根据编号 创建小孩
			Boy boy = new Boy(i);
			// 如果是第一个小孩
			if (i == 1) {
				first = boy;
				first.setNext(boy);// 构成环
				curBoy = first;// 让 curBoy指向第一个小孩
			}else {
				curBoy.setNext(boy);
				boy.setNext(first);
				curBoy = boy;
			}
		}
	}
	
	// 遍历环形链表
	public void list() {
		if (first == null) {
			System.out.println("当前没有小孩可以吃 ");
			return;
		}
		Boy curBoy = first;
		while (true) {
			System.out.printf("当前小孩编号%d\n",curBoy.getNo());
			if (curBoy.getNext() == first) {
				// 说明遍历完了 退出
				break;
			}
			curBoy = curBoy.getNext();
		}
	}
	
	// 从第k哥小孩开始隔n个小孩报数 输出报数的小孩然后吃掉
	/**
	 * 
	 * @param nums 表示最开始圈里有多少小孩
	 * @param k 从第k个小孩开始
	 * @param n 隔几个小孩报数
	 */
	public void getBoy(int nums,int k,int n) {
		Boy helper = first;// 辅助指针 指向环形链表的最后节点
		while (true) {
			// 将helper指向当前链表最后一个结点
			if (helper.getNext() == first) {
				break;
			}
			helper = helper.getNext();
		}
		// 小孩报数前 让first 和 helper 移动 k-1
		for (int i = 0; i < k - 1; i++) {
			first = first.getNext();
			helper = helper.getNext();
		}
		// 小孩报数时 让first 和 helper 移动 n-1
		while (true) {
			if (helper == first) {
				// 链表里只有一个数据
				break;
			}
			for (int i = 0; i < n - 1; i++) {
				first = first.getNext();
				helper = helper.getNext();
			}
			// 这时first指向的节点 就是要出圈的小孩节点
			System.out.printf("出圈的小孩是%d\n",first.getNo());
			first = first.getNext();
			helper.setNext(first);
		}
		System.out.printf("最后留在圈中的小孩编号%d \n", first.getNo());
	}
	
}
// 链表节点
class Boy{
	private int no;
	private Boy next;
	
	public Boy(int no) {
		this.no = no;
	}

	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public Boy getNext() {
		return next;
	}

	public void setNext(Boy next) {
		this.next = next;
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值