链表的基本特性
1.链表是一种链式结构
2.链表在逻辑上是有序的
3.链表在实际的内存中不一定是连续的
4.链表有带头结点和不带头结点的两种实现
本文主要讲解单向带头结点的链表增删改查操作
链表的逻辑结构图
链表的内存示意图
代码实现
如果代码不太好理解,可以自己画一下流程图
比如顺序插入时
package com.structure.linked;
import java.util.LinkedList;
import java.util.Stack;
/**
* @author zzq
* @Date 2021-07-20 9:04
*/
public class SingleLinkedListDemo {
public static void main(String[] args) {
HeroNode head1 = new HeroNode(1,"宋江","及时雨");
HeroNode head2 = new HeroNode(2,"吴用","智多星");
HeroNode head3 = new HeroNode(3,"卢俊义","玉麒麟");
HeroNode head4 = new HeroNode(4,"武松","行者");
SingleLinkedList singleLinkedList = new SingleLinkedList();
// singleLinkedList.add(head1);
// singleLinkedList.add(head4);
// singleLinkedList.add(head3);
// singleLinkedList.add(head2);
singleLinkedList.addByOrder(head4);
singleLinkedList.addByOrder(head3);
singleLinkedList.addByOrder(head1);
singleLinkedList.addByOrder(head2);
singleLinkedList.showList();
;
//System.out.println("链表元素个数----"+singleLinkedList.getCount());
//System.out.println("获取元素----"+singleLinkedList.getIndex(3));
//SingleLinkedList reverse = singleLinkedList.reverse(singleLinkedList.head);
System.out.println("反转后------------------------");
//singleLinkedList.reversePrintV4(singleLinkedList.head);
//singleLinkedList.showList();
//singleLinkedList.showList();
//HeroNode head5 = new HeroNode(3,"小卢","墨玉麒麟");
//singleLinkedList.update(head5);
//System.out.println("修改后-----------------------");
//singleLinkedList.showList();
}
/**
* 将两个有序的链表合并成一个有序的链表
* @param head1
* @param head2
* @return
*/
public static HeroNode merge(HeroNode head1,HeroNode head2){
return null;
}
}
class SingleLinkedList {
/**
* 链表头,头节点不存储数据,只作为一个遍历的起始点
*/
HeroNode head = new HeroNode(0,"","");
public void add(HeroNode node){
//借用一个辅助变量。 head一定不能动,否则链表会出现问题
HeroNode temp = head;
//插入时,从后面插入,找到最后的一个节点,插在这个元素的后面
while (true){
if(temp.next == null){
//说明找到最后一个了
break;
}
//继续往后找
temp = temp.next;
}
//循环结束,说明找到了最后的一个节点,将这个元素追加在temp后面
temp.next = node;
}
/**
* 按顺序添加
* @param node
*/
public void addByOrder(HeroNode node){
HeroNode temp = head;
//因为是单向链表,所以要找到待插入节点的前一个节点,否则链表无法串起来
//按顺序插入时,要先找到他前面那个节点,然后跟在它后边
//标识待加入的是否存在
boolean flag = false;
while (true){
//到链表的最后了
if(temp.next == null){
break;
}
//位置找到,就在temp的后面
if(temp.next.no > node.no){
break;
}else if(temp.next.no == node.no){
//说明重复了
System.out.println("要插入的数据已经存在----"+node.no);
flag = true;
break;
}
//后移,遍历链表
temp = temp.next;
}
if(flag){
return;
}
//说明node 应该在temp.next 前面 那么应该插在temp 和 temp.next 中间
node.next = temp.next;
temp.next = node;
}
/**
* 删除
* @param node
* @return
*/
public boolean delete(HeroNode node){
HeroNode temp = head;
//head == node 判断
while (true){
if(temp.next == null){
//说明找到最后一个了
System.out.println("删除失败,未找到待删除的元素----"+node.no);
return false;
}
if(temp.next.no == node.no){
//找到了,删除该节点 其实就是改变一下引用关系
temp.next = temp.next.next;
System.out.println("找到待删除的元素----"+node.no);
return true;
}
//继续往后找
temp = temp.next;
}
}
/**
* 修改
* @param node
* @return
*/
public boolean update(HeroNode node){
//头结点永远不动,只用来遍历
HeroNode temp = head;
//head == node 判断
while (true){
if(temp.next == null){
//说明找到最后一个了
System.out.println("修改失败,未找到待修改的元素----"+node.no);
return false;
}
if(temp.no == node.no){
//找到了
temp.name = node.name;
temp.nickName = node.nickName;
System.out.println("找到待删除的元素----"+node.no);
return true;
}
//继续往后找
temp = temp.next;
}
}
public void showList(){
if(head.next == null){
System.out.println("链表是空的");
return;
}
//借用一个辅助变量。 head一定不能动,否则链表会出现问题
HeroNode temp = head.next;
//插入时,从后面插入,找到最后的一个节点,插在这个元素的后面
while (true){
if(temp.next == null){
//说明找到最后一个了
System.out.println(temp.toString());
break;
}
//继续往后找
System.out.println(temp.toString());
temp = temp.next;
}
}
/**
* 1.计算链表有效元素的个数
* 遍历一下就可以得到
* 参数这里没写,可以传一个head节点,或者一个SingleLinkedList进来(获取他的头节点)
*/
public int getCount(){
//不算头节点
HeroNode temp = head.next;
int count = 0;
while (temp != null){
count++;
if(temp.next == null){
break;
}
temp = temp.next;
}
return count;
}
/**
* 2.获取链表倒数第k个节点
* 思路:
* 1.假设链表长度为10,那么倒数第三个节点就是 10 9 8 第8个位置的元素
* 即倒数第K个节点实际是正向链表的第 length - k + 1 个位置的元素
* @param index
* @return
*/
public HeroNode getIndex(int index){
int count = getCount();
if(index <= 0 || index > count) {
return null;
}
//遍历,找到第 length - k + 1 位置的元素
HeroNode temp = head.next;
for (int i = 0; i < count - index; i++) {
temp = temp.next;
}
return temp;
}
/**
* 链表反转
* @param head
* @return
*/
public SingleLinkedList reverse(HeroNode head){
HeroNode temp = head.next;
SingleLinkedList singleLinkedList = new SingleLinkedList();
HeroNode temp1 = singleLinkedList.head;
while (true){
if(temp==null){
break;
}
HeroNode node = temp1.next;
HeroNode test = new HeroNode(temp.no,temp.name, temp.nickName);
temp1.next = test;
test.next = node;
temp = temp.next;
}
return singleLinkedList;
}
/**
* 链表反转
* @param head
*/
public void reverseV2(HeroNode head){
if(head.next == null || head.next.next == null){
//为空或者只有一个节点,直接返回
return;
}
HeroNode reverseHead = new HeroNode(0,"","");
//当前节点,从head.next开始遍历
HeroNode cur = head.next;
//当前节点的下一个节点
HeroNode next = null;
while (cur != null){
next = cur.next;
cur.next = reverseHead.next;
reverseHead.next = cur;
cur = next;
}
head.next = reverseHead.next;
}
/**
* 使用栈协助链表反转,栈是先进后出的,所以直接遍历链表压栈,然后出栈即可
* @param head
*/
public void reversePrint(HeroNode head){
if(head.next == null || head.next.next == null){
//为空或者只有一个节点,直接返回
return;
}
HeroNode cur = head.next;
Stack<HeroNode> stack = new Stack<>();
while (cur != null) {
stack.push(cur);
cur = cur.next;
}
while (!stack.isEmpty()){
System.out.println(stack.pop());
}
}
}
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;
}
public HeroNode(int no, String name, String nickName, HeroNode next) {
this.no = no;
this.name = name;
this.nickName = nickName;
this.next = next;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}