您的位置:首页 > 编程语言 > Java开发

单双向链表的增删改查等详细操作(Java实现)

2020-07-15 06:07 260 查看

单链表是一种常见的链式存取的数据结构,且十分容易理解。

概念

链表的每一个元素在内存中都是分散的,不像数组那样必须一次排列。为了串联不同的节点,每个节点除了有个数据域(data)外,还必须要有一个 “指针” 域(next)指向下一个节点。此外,我们可以设置一个头结点来引出整个链表,头结点的数据域可以存null,也可以存与链表有关的内容,比如表长。显然最后一个节点的next指向null。

节点的创建

要想组成一个链表,则必须先有个最小单位的节点。为了直观,这个举例节点的数据域元素为编号和城市名称。
(所有代码均在一个LinkedList类中)

/**
* 在LinkedList类中定义节点
*/
static class CityNode{
public int number;//城市排名
public String name;//城市名称
public CityNode next;//指向下一个节点
//构造器
public CityNode(int num, String name){
this.number = num;
this.name = name;
}

//重写toString方法便于观察
@Override
public String toString() {
return "CityNode{" +
"number=" + number +
"-- name=" + name + '}';
}

}

链表的创建

一条空链表创建时仅仅只需创建一个头结点。但是这条空链表还不能进行增删改查等操作,需要各种添加方法。
在LinkedList类中添加一个CityList类。

static class CityList{
//头结点不放数据
private CityNode head = new CityNode(0,"head");
}

各种操作的实现方法

紧接着在CityList类中添加方法

  • 增加节点
  1. 无序添加
    无序添加只需要遍历到最后一个节点,并且使其指向要添加的节点。
/**
*添加节点
*/
public void add(CityNode node){
CityNode temp = head;//每次都temp指向头结点
while (temp.next != null){
temp = temp.next;//让temp一直遍历到最后一个节点
}
//将最后一个节点的next指向要添加的节点
temp.next = node;
}
  1. 有序添加
    如果元素有编号,例如例子中的城市number,则需要按照顺序来插入节点。
    一开始temp必须指向head,因为必须要拿temp的next节点去和要插入的节点node比较。否则当node比temp大,node就没办法插入到temp的前面去了,因为单向链表只能向后操作。
public void addByOrder(CityNode node){
//定义一个临时节点
CityNode temp = head;
//当下一个节点不为空时进入循环
while (temp.next != null){
//编号如果一样则直接返回
if (temp.number == node.number){
System.out.printf("节点%d已存在\n",node.number);
return;
}
if (temp.next.number > node.number){
//此时temp为要插入位置的前一个节点
node.next = temp.next;
temp.next = node;
return;//操作完成直接返回
}
temp = temp.next;//向后移一个节点
}
//如果while执行完则说明插入的节点编号位于链表末尾或者编号和链表最后一个节点编号相同
if ( temp.number == node.number){
System.out.printf("节点%d已存在\n",node.number);
}else {
temp.next = node;
}
}
  • 删除节点

删除节点一对先找到要删除节点的上一个节点,直接让temp的next指向temp.next.next,跨过删除的节点

/**
* 删除节点
* @param num
* @return是否删除成功
*/
public boolean delete(int num){
//flag表示删除是否成功
boolean flag = false;
CityNode temp = head;
if (temp.next == null){
System.out.println("链表为空 !");
return false;
}
while (temp.next != null){
//找到了要删除节点的上一个节点
if (temp.next.number == num ){
//如果要删除的节点是最后一个,直接让temp.next指向null
if (temp.next.next == null){
temp.next = null;
System.out.printf("节点%d已经删除\n",num);
flag = true;
break;
}else {
temp.next = temp.next.next;
System.out.printf("节点%d已经删除\n",num);
flag = true;
break;
}
}
temp = temp.next;//向后移一位
}//如果遍历结束flag还是false
if (!flag)
System.out.println("要删除的节点不存在");
return flag;
}
  • 打印节点

打印节点比较简单,一个循环就可以。这里返回有效节点个数

/**
* 遍历显示链表。
* @return 返回值是链表有效节点个数
*/
public int showList(){
int length = 0;
if (head.next == null){
System.out.println("链表为空");
return 0;
}
CityNode temp = head;//临时节点
while (temp.next != null){
temp = temp.next;
System.out.println(temp);
length++;
}
return length;
}
  • 修改节点

修改节点只需要遍历找到需要修改的节点并且值交换就行了。

/**
* 更新节点,返回值为是否修改成功
*/
public boolean update(CityNode node){
//flag判断找没找到节点
boolean flag = false;
CityNode temp = head;
//空链表
if (head.next == null){
System.out.println("链表为空");
return false;
}
while (temp.next != null){
temp = temp.next;
//找到了
if (temp.number == node.number){
//值交换
temp.name = node.name;
flag = true;
break;
}
}
return flag;
}
  • 其他操作
  1. 反转链表

    规律是每次插入时都在新链表的头结点和第一个有效节点之间插入
/**
* 反转链表
* @param list
* @return 反转后的链表
*/
public CityList reverseList(CityList list){
//创建一个新的链表头结点
CityNode reverseNode = new CityNode(0,"");
//cur指向第一个有效链表元素
CityNode cur = list.head.next;
//            如果链表为空或者只有一个有效元素则直接返回
if (cur == null || cur.next.next == null)
return list;
CityNode next;//next指向的是cur的下一个元素
while (cur != null){
next = cur.next;
cur.next = reverseNode.next ;
reverseNode.next = cur;
//cur后移一位
cur = next;
}
//            list的下一位指向反转后的链表头的下一个元素
list.head.next = reverseNode.next;
return list;
}

2.合并两个有序链表

合并有序链表可以把两个链表的节点互相比较,然后加入新链表中

/**
* 合并两个有序链表
* @param list
* @param list1
* @return 合并后新的链表
*/
public CityList combine(CityList list,CityList list1){
//创建一个新链表
CityList list2 = new CityList();
//分别设置三个临时辅助节点
CityNode temp2 = list2.head;
CityNode temp = list.head.next;
CityNode temp1 = list1.head.next;
while (temp !=null && temp1!= null){
if (temp.number < temp1.number){
//将小的节点添加到新链表后面
temp2.next = temp;
//新链表辅助节点后移
temp2 = temp2.next;
//辅助节点后移
temp = temp.next;
}else {
temp2.next = temp1;
temp2 = temp2.next;
temp1 = temp1.next;
}
}
//如果短的链表已经完全合并,只需要将剩下部分的另一个链表接上
if (temp == null){
temp2.next = temp1;
}else {
temp2.next = temp;
}
return list2;
}

双链表

双向链表的节点比单链表的多了一个pre指针指向上一个节点,而且双向链表的更改节点方法和单链表是一样的

static class City_Node{
public int number;//城市排名(编号)
public String name;//城市名称(数据)
public City_Node next;//指向下一个节点
public City_Node pre; //指向上一个节点

@Override
public String toString() {
return "Node{" +
"number=" + number +
", name='" + name +
'}';
}

public City_Node(int number, String name) {
this.number = number;
this.name = name;
}
}
  • 添加节点
    添加方法多了对pre的赋值
/**
* 添加元素
*/
public void add(City_Node node){
City_Node temp = head;
while (temp.next != null){
temp = temp.next;
}
temp.next = node;
node.pre = temp;
}
  • 删除节点
    删除节点不需要找到要删除节点的上一个了,直接找到要删除的节点便可,因为可以根据pre和next对相邻的节点进行操作。
/**
* 删除节点
*/
public void delete(int num){
City_Node temp = head.next;
//空链表
if (temp == null){
System.out.println("空链表");
return;
}
while (temp.next != null){
if (temp.number == num){
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
return;
}
temp = temp.next;
}
//如果要删除的链表在末尾
if (temp.number == num){
temp.pre.next = null;
}else {
System.out.println("节点不存在");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: