数据结构与算法(Java)之链表
Posted 达少Rising
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法(Java)之链表相关的知识,希望对你有一定的参考价值。
单向链表
package com.weeks.linkedlist;
import java.util.Stack;
/**
* @author 达少
* @version 1.0
* 实现链表,保存水浒传英雄人物
*/
public class LinkedListDemo {
public static void main(String[] args) {
//创建英雄任务
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "武松", "行者");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
HeroNode hero5 = new HeroNode(5, "关胜", "大刀");
HeroNode hero6 = new HeroNode(6, "戴宗", "神行太保");
HeroNode hero7 = new HeroNode(7, "李逵", "黑旋风");
//创建链表
// LinkedList linkedList = new LinkedList();
// //添加数据到链表
// linkedList.push(hero1);
// linkedList.push(hero4);
// linkedList.push(hero2);
// linkedList.push(hero3);
//按id升序插入
// linkedList.pushOrderById(hero1);
// linkedList.pushOrderById(hero4);
// linkedList.pushOrderById(hero3);
// linkedList.pushOrderById(hero2);
// //显示链表
// System.out.println("======原来的链表======");
// linkedList.list();
/*
//修改节点
linkedList.update(new HeroNode(2, "吴用", "智多星"));
System.out.println("修改后的链表~~~~");
linkedList.list();
//删除节点
linkedList.delete(1);
// linkedList.delete(4);
linkedList.delete(3);
// linkedList.delete(2);
// linkedList.delete(5);
System.out.println("删除节点后的链表~~~");
linkedList.list();
//测试获取链表测长度
int size = LinkedList.getSize(linkedList.getHead());
System.out.println("当前链表的长度为:" + size);
//测试返回倒数第k个元素
int k = 2;
HeroNode lastK = LinkedList.findLastK(linkedList.getHead(), k);
System.out.println("链表的倒数第" + k + "个元素是:" + lastK);
*/
//反转链表
// LinkedList.reverse(linkedList.getHead());
// System.out.println("======反转后的链表======");
// linkedList.list();
//逆序输出链表元素
// System.out.println("======逆序输出链表元素(不改变链表的结构~)======");
// LinkedList.reversePrint(linkedList.getHead());
//创建两个链表
LinkedList linkedList1 = new LinkedList();
LinkedList linkedList2 = new LinkedList();
//向两个链表中添加数据
linkedList1.pushOrderById(hero1);
linkedList1.pushOrderById(hero3);
linkedList1.pushOrderById(hero5);
linkedList1.pushOrderById(hero7);
linkedList2.pushOrderById(hero2);
linkedList2.pushOrderById(hero4);
linkedList2.pushOrderById(hero6);
System.out.println("原来的链表1:");
linkedList1.list();
System.out.println("原来的链表2:");
linkedList2.list();
//合并两个链表
HeroNode merge = LinkedList.merge(linkedList1.getHead(), linkedList2.getHead());
LinkedList mergeLinkedList = new LinkedList(merge);
System.out.println("合并后的链表:");
mergeLinkedList.list();
System.out.println("原来的链表1:");
linkedList1.list();
System.out.println("原来的链表2:");
linkedList2.list();
}
}
class LinkedList {
//头节点,不存储具体的数据
private HeroNode head = new HeroNode(0, "", "");
public LinkedList(){}
public LinkedList(HeroNode head){
this.head = head;
}
public HeroNode getHead() {
return head;
}
//添加节点(尾插法)
public void push(HeroNode heroNode) {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head;
//遍历链表
while (true) {
//判断链表是否为空,为空则退出循环
if (p.next == null) {
break;
}
//不空则p向后移,直到找到尾节点
p = p.next;
}
//当退出循环p已经指向尾节点,在p后添加节点
p.next = heroNode;
}
//添加节点的时候要按id的顺序升序添加,如果id已经存在则提示:加入的英雄人物编号已经存在不能添加
public void pushOrderById(HeroNode heroNode) {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head;
boolean flag = false;//标志添加的id英雄任务是否已经存在,默认不存在为false
//首先要找到添加的位置
while (true) {//循环遍历,找到添加的位置
if (p.next == null) {//说明已经到了尾节点
break;
}
if (p.next.id > heroNode.id) {//说明已经找到插入的位置,位与p与p.next之间
break;
}
if (p.next.id == heroNode.id) {//说明id已存在
flag = true;
break;
}
//其他情况将p后移
p = p.next;
}
//如果不存在则将节点添加到指定的位置
if (!flag) {
heroNode.next = p.next;
p.next = heroNode;
} else {
System.out.println("加入的英雄人物编号已经存在不能添加!!!");
}
}
//修改链表节点的信息,根据id查找节点,所以id不能改
public void update(HeroNode newHeroNode) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空,没有节点可以修改!!!");
return;
}
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head.next;
boolean flag = false;//标志是否找到要修改的节点
//循环遍历俩链表找到要修改的节点
while (true) {
if (p == null) {//说明已经遍历完整链表
break;
}
if (p.id == newHeroNode.id) {
flag = true;
break;
}
//其他情况p后移
p = p.next;
}
//找到修改该的节点后进行修改
if (flag) {
//更改信息
p.name = newHeroNode.name;
p.nickName = newHeroNode.nickName;
} else {
System.out.printf("没有找到id为%d的节点,本次修改不成功!", newHeroNode.id);
}
}
//根据id删除节点
public void delete(int id) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空,没有节点可以删除!!!");
return;
}
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head;
boolean flag = false;//标记是否找到要删除节点的前一个节点
//遍历链表
while (true) {
if (p.next == null) {//说明已经遍历到了尾节点
break;
}
if (p.next.id == id){//说明找到了要删除的节点的前一个节点
flag = true;
break;
}
//其他情况后移
p = p.next;
}
//删除节点
if(flag){
p.next = p.next.next;
}else{
System.out.printf("id为%d的节点不存在,本次删除操作失败!", id);
}
}
//遍历显示整个链表
public void list() {
//要在尾部添加节点,首先要找到尾部节点
//因为头节点不能动,所以需要临时变量来遍历链表
HeroNode p = head.next;
//遍历链表
while (true) {
//判断链表是否为空,为空则退出循环
if (p == null) {
break;
}
//显示节点
System.out.println(p);
//后移
p = p.next;
}
}
//获取链表的长度
public static int getSize(HeroNode head){
//定义变量保存有效节点的个数(头节点不是有效节点)
int size = 0;
//定义变量作为游标,遍历链表
HeroNode cur = head.next;
//遍历链表
while(cur != null){
size++;
//cur后移
cur = cur.next;
}
return size;
}
//获取单链表的倒数第k个节点元素【新浪面试题】
/**
* 分析:
* 1.首先要知道这个链表的总长度size,可以使用getSize()获取
* 2.获取要遍历的长度为size-k
* 3.返回倒数第k个元素
*/
public static HeroNode findLastK(HeroNode head, int k){
//判断链表是否为空
if(head.next == null){
return null;
}
//获取当前链表的长度
int size = getSize(head);
//判断输入的k值是否符合要求
if(k <= 0 || k > size){
return null;
}
//定义临时变量遍历链表
HeroNode cur = head.next;
//遍历前size-k个元素
for (int i = 0; i < (size - k); i++) {
//后移cur
cur = cur.next;
}
return cur;
}
//实现单链表的反转【腾讯的面试题】
/**
* 分析:
* 1.定义一个头节点reverseHead
* 2.从原来的链表中遍历获取节点,每获得一个节点就将其从原来的链表中摘取下来
* 3.将摘取下来的节点插入到新的头节点的下一个位置(头插法)
* 4.最后head.next = reverse.next
*/
public static void reverse(HeroNode head){
//判断当前链表是否为空或者是否只有一个元素
if(head.next == null || head.next.next == null){
return;
}
//定义两个变量,分别指向原来链表的当前节点和当前节点的下一个节点
HeroNode cur = head.next;
HeroNode next = null;
//定义新的头节点
HeroNode reverseHead = new HeroNode(0, "", "");
//遍历原来的链表
while(cur != null){
//将next指向当前节点的下一个节点,记录下一个摘取位置
next = cur.next;
//将cur从原来的列表中摘取下来
head.next = cur.next;
//将摘取下来的节点插入新的头节点的下一个位置
cur.next = reverseHead.next;
reverseHead.next = cur;
//将原来链表的下一个节点赋值给cur
cur = next;
}
//最后将新的头节点next赋值给原来的头节点next,完成反转
head.next = reverseHead.next;
}
//将单链表按原来的顺序逆序打印出来【百度面试题】
/**
* 分析:
* 方式1:将原来的链表进行反转后再进行遍历操作,但是这会改变链表的结构,所以不推荐
* 方式2:利用栈的先进后出特性,反向打印链表,不改变链表的原来结构
*/
public static void reversePrint(HeroNode head){
//判断链表是否为空
if (head.next == null){
return;
}
//定义一个栈
Stack<HeroNode> stack = new Stack<>();
//定义游标遍历链表
HeroNode cur = head.next;
//遍历链表
while(cur != null){
//将当前节点压入栈中
stack.push(cur);
//将cur后移
cur = cur.next;
}
//输出栈中的元素
while(stack.size() > 0){
System.out.println(stack.pop());
}
}
//合并两个有序链表,并保证合并后的链表还是有序的(按id大小升序排序),不能破坏原来的两个链表(深拷贝)
/**
* 分析:
* 1.新建一个头节点:newHead
* 2.比较两个链表的元素id大小,将id小的链表节点添加到新的的链表中
* 3.返回新的链表头节点
*/
public static HeroNode merge(HeroNode head1, HeroNode head2){
//判断链表1,2是否为空
if(head1.next == null && head2.next == null){
return null;
}
if(head1.next == null){
return head2;
}
if(head2.next == null){
return head1;
}
//定义新的头节点
HeroNode new_head = new HeroNode(0, "", "");
//定义两个游标,遍历两个链表
HeroNode cur1 = head1.next;
HeroNode cur2 = head2.next;
//定义变量存储要插入到新链表的元素
HeroNode temp = null;
//定义变量记录新链表的尾部节点
HeroNode rear = new_head;
while(cur1 != null && cur2 != null){
if(cur1.id < cur2.id){
temp = new HeroNode(cur1);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur1向后移
cur1 = cur1.next;
}else if(cur1.id > cur2.id){
temp = new HeroNode(cur2);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur2向后移
cur2 = cur2.next;
}else {//这种情况是cur1.id==cur2.id,所以只允许插入其中一个,选择插入cur1
temp = new HeroNode(cur1);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur1和cur2都要向后移
cur1 = cur1.next;
cur2 = cur2.next;
}
}
//当其中有一个链表还没遍历完,要继续遍历添加到新链表中
while(cur1 != null){
temp = new HeroNode(cur1);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur1和cur2都要向后移
cur1 = cur1.next;
}
while(cur2 != null){
temp = new HeroNode(cur2);
//将取出的元素插入到新链表的尾部
rear.next = temp;
//rear向后移
rear = temp;
//cur2向后移
cur2 = cur2.next;
}
return new_head;
}
}
class HeroNode {
public int id;
public String name;
public String nickName;
public HeroNode next;
public HeroNode(int id, String name, String nickName) {
this.id = id;
this.name = name;
this.nickName = nickName;
}
//深拷贝方法
public HeroNode(HeroNode heroNode){
this.id = heroNode.id;
this.name = heroNode.name;
this.nickName = heroNode.nickName;
this.next = null;
}
@Override
public String toString() {
return "HeroNode{" +
"id=" + id +
", name='" + name + '\\'' +
", nickName='" + nickName + 以上是关于数据结构与算法(Java)之链表的主要内容,如果未能解决你的问题,请参考以下文章