什么是链表?
1,链表是以节点的方式存储的,链式存储
2,每个节点包含data域,next域指向下一个节点
3,链表的各个节点不一定是链式存储的
4,链表分带头节点的链表和不带头结点的链表
下面用代码实现一个单向链表
首先定义一个类用来存储到链表中
/**
* 定义HeroNode,每个HeroNode对象就是一个节点
*/
public class HeroNode {
public int no;//编号
public String name;//名字
public String nickName;//昵称
public HeroNode next;//指向的下一个节点
public HeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
链表实现类
/**
* 定义SingleLinkedList管理我们的英雄
*/
public class SingleLinkedList {
//初始化一个头节点,头结点不动,不存放任何数据
public HeroNode head = new HeroNode(0, "", "");
/**
* 添加节点到单向链表
* 当不考虑编号顺序时
* 1,找到当前链表的最后节点
* 2,将最后这个节点的next指向新的节点
*
* @param heroNode
*/
public void add(HeroNode heroNode) {
//因为头结点不能动,因此我们需要一个辅助遍历的temp
HeroNode temp = head;
//遍历链表,找到最后的节点
while (true) {
//如果指向下一个链表是null 就是最后一个
if (temp.next == null) {
break;
}
//如果不是空 就是还没到最后一个,将temp后移
temp = temp.next;
}
//当退出while循环时,temp就指向了链表的最后
//将最后这个节点的next指向新的节点即可
temp.next = heroNode;
}
//第二种插入方式在添加英雄时,根据排名将英雄输入到指定位置
public void addByOrder(HeroNode heroNode) {
//使用辅助变量
//我们找的temp是位于添加位置的前一个节点
HeroNode temp = head;
boolean flag = false;//如果要添加的编号已存在flag就变成true
while (true) {
if (temp.next == null) {//说明已经到了链表的最后
break;
}
if (temp.next.no > heroNode.no) {//如果temp的下一个节点的编号大于要插入节点的编号,位置找到,在temp的后面插入
break;
}else if(temp.next.no == heroNode.no){//编号相同 已存在
flag = true;
break;
}
//三种条件都不满足,指针后移
temp = temp.next;
}
if(flag){//已存在 不能添加
System.out.println("编号"+heroNode.no+"已存在");
}else{
heroNode.next = temp.next;
temp.next = heroNode;
}
}
//根据编号修改
public void edit(HeroNode newHeroNode){
HeroNode temp = head.next;//直接指向头结点的下一个
boolean flag = false;//表示是否找到节点
while(true){
if(temp == null){
break;//表示已经遍历完链表
}
if(temp.no == newHeroNode.no){
//找到
flag = true;
break;
}
temp = temp.next;
}
if(flag){
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}else{
System.out.println("没有找到编号"+newHeroNode.no+"的英雄");
}
}
//删除节点
public void delete(int no){
//辅助变量
HeroNode temp = head;
boolean flag = false;//标志是否找到待删除节点
while(true){
if(temp.next == null){
break;
}
if(temp.next.no == no){
//找到了待删除节点的前一个节点temp
flag = true;
break;
}
//后移
temp = temp.next;
}
if(flag){
//跳过要删除的节点,jvm垃圾回收机制会清理没有作用的节点
temp.next = temp.next.next;
}else{
System.out.println("要删除的节点不存在");
}
}
//显示链表
public void show() {
//如果链表为空就直接返回
if (head.next == null) {
System.out.println("链表为空");
return;
}
//再次使用一个辅助变量来遍历
HeroNode temp = head.next;
while (true) {
//判断链表是否到最后
if (temp == null) {
break;
}
//输出节点信息
System.out.println(temp);
//将temp后移
temp = temp.next;
}
}
}
问题1,统计链表中有效节点的个数
public static int getLength(SingleLinkedList link){
if(link.head.next == null){
//空链表 返回0
return 0;
}
int length = 0;
HeroNode current = link.head;
//头结点不是有效节点 不统计头结点
while(current.next != null){
length += 1;
current = current.next;
}
return length;
}
问题2,获取链表中倒数第k个元素的值
public static HeroNode findLastIndexNode(SingleLinkedList link,int index){
//如果链表为空 直接返回null 没找到
if(link.head.next == null){
return null;
}
//遍历获取链表的有效个数,调用刚才的方法
int size = getLength(link);
//校验数据是否合法
if(index <= 0 || index > size){
return null;
}
//遍历size-index的位置,就是倒数第k个节点
HeroNode current = link.head.next;
for(int i = 0;i < size-index;i++){
current = current.next;
}
return current;
}
问题3,实现单链表的反转
public static void reverseList(SingleLinkedList link){
//如果链表为空,直接返回空
if(link.head.next == null){
return ;
}
//定义一个车辅助指针,帮助我们遍历原来的链表
HeroNode current = link.head.next;
HeroNode next = null;//当前节点的下一个节点
//定义一个新的头结点
HeroNode reverseHead = new HeroNode(0, "", "");
//遍历原来的链表,每遍历一个节点,就将其取出,并放在新的头结点的最前端
while (current != null){
next = current.next;//暂时保存当前节点的下一个节点
current.next = reverseHead.next;//将当前节点的下一个节点指向新的连别的最前端
reverseHead.next = current;//再将新的链表的最前端指向当前节点
current = next;//当前节点后移
}
//原来的头结点指向新的头结点,实现反转
link.head.next = reverseHead.next;
}
问题4,反向打印单链表
对于这个问题,我们可以对链表先进行反转然后再遍历输出,但是这样会破坏原来链表的结构,没必要。
所以采取另外一种方法,利用栈的先进后出的特性,把链表中的元素压入栈中在取出打印。
public static void reversePrint(SingleLinkedList link){
if(link.head.next == null){
return ;
}
Stack<HeroNode> stack = new Stack<>();
HeroNode current = link.head.next;
while(current != null){
stack.push(current);//添加到栈
current = current.next;//后移
}
while (!stack.empty()){
System.out.println(stack.pop());
}
}
双向链表的定义及基本操作
首先定义一个节点类
public class HeroNode2 {
public int no;//编号
public String name;//名字
public String nickName;//昵称
public HeroNode2 next;//指向当前节点的下一个节点
public HeroNode2 pre;//指向当前节点的前一个节点
public HeroNode2(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
双向链表
public class DoubleLinkedList {
//初始化一个头节点,头结点不动,不存放任何数据
public HeroNode2 head = new HeroNode2(0, "", "");
//显示链表
public void show() {
//如果链表为空就直接返回
if (head.next == null) {
System.out.println("链表为空");
return;
}
//再次使用一个辅助变量来遍历
HeroNode2 temp = head.next;
while (true) {
//判断链表是否到最后
if (temp == null) {
break;
}
//输出节点信息
System.out.println(temp);
//将temp后移
temp = temp.next;
}
}
//在双向链表的最后添加一个节点
public void add(HeroNode2 heroNode) {
//因为头结点不能动,因此我们需要一个辅助遍历的temp
HeroNode2 temp = head;
//遍历链表,找到最后的节点
while (true) {
//如果指向下一个链表是null 就是最后一个
if (temp.next == null) {
break;
}
//如果不是空 就是还没到最后一个,将temp后移
temp = temp.next;
}
//当退出while循环时,temp就指向了链表的最后
temp.next = heroNode;
heroNode.pre = temp;
}
//根据编号修改
public void edit(HeroNode2 newHeroNode){
HeroNode2 temp = head.next;//直接指向头结点的下一个
boolean flag = false;//表示是否找到节点
while(true){
if(temp == null){
break;//表示已经遍历完链表
}
if(temp.no == newHeroNode.no){
//找到
flag = true;
break;
}
temp = temp.next;
}
if(flag){
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}else{
System.out.println("没有找到编号"+newHeroNode.no+"的英雄");
}
}
//删除节点
//找到待删除的节点,自我删除即可
public void delete(int no){
//辅助变量
HeroNode2 temp = head.next;
boolean flag = false;//标志是否找到待删除节点
while(true){
if(temp == null){
break;
}
if(temp.no == no){
//找到了待删除节点的前一个节点temp
flag = true;
break;
}
//后移
temp = temp.next;
}
if(flag){
temp.pre.next = temp.next;
//如果要删除的是最后一个节点,就不能执行下面一句操作(待删除节点后一个节点的pre指向待删除节点的前一个节点)
if(temp.next != null){
temp.next.pre = temp.pre;
}
}else{
System.out.println("要删除的节点不存在");
}
}
//按顺序添加
public void addByOrder(HeroNode2 node){
//使用辅助变量
//我们找的temp是位于添加位置的前一个节点
HeroNode2 temp = head;
boolean flag = false;//如果要添加的编号已存在flag就变成true
while (true) {
if (temp.next == null) {//说明已经到了链表的最后
break;
}
if (temp.next.no > node.no) {//如果temp的下一个节点的编号大于要插入节点的编号,位置找到,在temp的后面插入
break;
}else if(temp.next.no == node.no){//编号相同 已存在
flag = true;
break;
}
//三种条件都不满足,指针后移
temp = temp.next;
}
if(flag){//已存在 不能添加
System.out.println("编号"+node.no+"已存在");
}else{
node.next = temp.next;
node.pre = temp;
temp.next = node;
temp.next.pre = node;
}
}
}