文章目录
1、定义单向链表中每个结点的数据结构
package common;
/**
* @author zhihua.li
* @date 2021/1/20 - 10:16
**/
public class Node {
private Integer no;
private String name;
private String nickName;
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
private Node next;
public Node(Integer no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
public Node next(){
return next;
}
@Override
public String toString() {
return "common.Node{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
2、单向链表的初始化与操作
1、初始化实质上就是设置一个头结点
public Node headNode = new Node(0, "", "");
2、添加结点到单向链表中
/*
添加节点到链表中,直接添加到链表尾部
找到当前链表的最后一个节点,并将其指针域指向要添加的新节点
*/
public void addNode(Node node) {
Node temp = headNode;
// 找到当前链表的最后一个元素
while (true) {
if (temp.next() == null) {
break;
}
temp = temp.next();
}
// 设置其指针域为目标节点
temp.setNext(node);
}
3、 每次将结点添加到到链表的头结点之后,保证新添加的节点总是链表头结点的下一个结点
// 将结点每次都添加到链表头结点的下一个节点
public void addToHead(Node node) {
Node temp = headNode;
if (temp.next() == null) {
temp.setNext(node);
return;
}
node.setNext(temp.next());
temp.setNext(node);
}
4、按序将结点添加到链表
// 插入链表的指定位置(按no顺序)
public void addToDes(Node node) {
// 设置工具节点,用来遍历链表
Node temp = headNode;
// 设置一个标志位,若为 true,说明插入失败
boolean flag = false;
// 找到当前链表的最后一个元素
while (true) {
// 找到链表尾,break
if (temp.next() == null) {
break;
}
// 如果当前遍历到的节点的下一位序号大于要插入的节点序号,说明插入位置就是当前遍历到的节点的下一位
if (temp.next().getNo() > node.getNo()) {
break;
// 要插入的节点的序号已经存在在链表中,就不能再往里面插入了
} else if (temp.next().getNo() == node.getNo()) {
flag = true;
break;
}
temp = temp.next();
}
// 无法插入的情况
if (flag) {
System.out.println("准备插入的节点已经在链表中存在,插入失败!");
}
// 成功插入的情况
else {
node.setNext(temp.next());
temp.setNext(node);
}
}
5、打印整个链表
// 通过遍历显示整个链表数据
public void listLinkedList() {
if (headNode.next() == null) {
System.out.println("链表为空");
return;
}
// 生成临时节点进行链表遍历
Node temp = headNode.next();
while (true) {
if (temp == null) {
break;
}
System.out.println(temp);
// 将temp后移
temp = temp.next();
}
}
6、根据结点序号对结点信息进行修改
// 根据节点的序号进行修改信息(注意:不能修改结点的no和next属性,均可能对其他元素产生影响)
public void updateNode(Node node) {
if (headNode.next() == null) {
System.out.println("链表为空");
return;
}
Node temp = headNode.next();
boolean flag = false;
while (true) {
// 链表已经遍历完
if (temp == null) {
break;
}
// 如果当前temp的no已经大于node的no了,在有序的情况下,以后也不可能有匹配的了,所以直接返回
if (temp.getNo() > node.getNo()) {
return;
}
// 找到了与修改节点no相同的结点
else if (temp.getNo() == node.getNo()) {
flag = true;
break;
}
temp = temp.next();
}
if (flag) {
temp.setName(node.getName());
temp.setNickName(node.getNickName());
} else {
// 没找到的情况
System.out.println("修改失败!");
}
}
7、删除结点
public void deleteNode(Integer no) {
Node temp = headNode;
boolean flag = false;
while (true) {
if (temp.next() == null) {
break;
}
if (temp.next().getNo() == no) {
flag = true;
break;
}
temp = temp.next();
}
if (flag) {
temp.setNext(temp.next().next());
} else {
System.out.println("删除成功!");
}
}
8、获取单链表长度
// 获取单链表长度(不统计头结点)
public int listLength() {
int length = 0;
Node node = headNode;
while (node.next() != null) {
length++;
node = node.next();
}
return length;
}
9、查找单链表中正数第n个结点
// 查找单链表中第k个节点
// (使用的时候index从1开始!)
public Node indexNode( Integer index) {
Node node = headNode.next();
int length = listLength();
if (index <= 0 || index > length) {
return null;
}
for (int i = 0; i < index-1; i++) {
node = node.next();
}
return node;
}
10、 查找单链表中倒数第n个结点
// 查找单链表中倒数第k个节点
public Node lastIndexNode(Integer index) {
Node node = headNode.next();
int length = listLength();
if (index <= 0 || index > length) {
return null;
}
// 倒数第一个从数组的角度考虑其实是第零个
for (int i = 0; i < length - index; i++) {
node = node.next();
}
return node;
}
小结:整个单链表操作类
package single_linked_list;
import com.sun.deploy.panel.ITreeNode;
import com.sun.xml.internal.ws.api.pipe.NextAction;
import common.Node;
/**
* @author zhihua.li
* @date 2021/1/20 - 10:16
* <p>
* 管理节点的类
**/
public class SingleLinkedList {
// 初始化头结点,头结点不存放具体的数据
public Node headNode = new Node(0, "", "");
/*
添加节点到链表中,直接添加到链表尾部
找到当前链表的最后一个节点,并将其指针域指向要添加的新节点
*/
public void addNode(Node node) {
Node temp = headNode;
// 找到当前链表的最后一个元素
while (true) {
if (temp.next() == null) {
break;
}
temp = temp.next();
}
// 设置其指针域为目标节点
temp.setNext(node);
}
// 将结点每次都添加到链表头结点的下一个节点
public void addToHead(Node node) {
Node temp = headNode;
if (temp.next() == null) {
temp.setNext(node);
return;
}
node.setNext(temp.next());
temp.setNext(node);
}
// 插入链表的指定位置(按no顺序)
public void addToDes(Node node) {
// 设置工具节点,用来遍历链表
Node temp = headNode;
// 设置一个标志位,若为 true,说明插入失败
boolean flag = false;
// 找到当前链表的最后一个元素
while (true) {
// 找到链表尾,break
if (temp.next() == null) {
break;
}
// 如果当前遍历到的节点的下一位序号大于要插入的节点序号,说明插入位置就是当前遍历到的节点的下一位
if (temp.next().getNo() > node.getNo()) {
break;
// 要插入的节点的序号已经存在在链表中,就不能再往里面插入了
} else if (temp.next().getNo() == node.getNo()) {
flag = true;
break;
}
temp = temp.next();
}
// 无法插入的情况
if (flag) {
System.out.println("准备插入的节点已经在链表中存在,插入失败!");
}
// 成功插入的情况
else {
node.setNext(temp.next());
temp.setNext(node);
}
}
// 通过遍历显示整个链表数据
public void listLinkedList() {
if (headNode.next() == null) {
System.out.println("链表为空");
return;
}
// 生成临时节点进行链表遍历
Node temp = headNode.next();
while (true) {
if (temp == null) {
break;
}
System.out.println(temp);
// 将temp后移
temp = temp.next();
}
}
// 根据节点的序号进行修改信息(注意:不能修改结点的no和next属性,均可能对其他元素产生影响)
public void updateNode(Node node) {
if (headNode.next() == null) {
System.out.println("链表为空");
return;
}
Node temp = headNode.next();
boolean flag = false;
while (true) {
// 链表已经遍历完
if (temp == null) {
break;
}
// 如果当前temp的no已经大于node的no了,在有序的情况下,以后也不可能有匹配的了,所以直接返回
if (temp.getNo() > node.getNo()) {
return;
}
// 找到了与修改节点no相同的结点
else if (temp.getNo() == node.getNo()) {
flag = true;
break;
}
temp = temp.next();
}
if (flag) {
temp.setName(node.getName());
temp.setNickName(node.getNickName());
} else {
// 没找到的情况
System.out.println("修改失败!");
}
}
// 删除节点
public void deleteNode(Integer no) {
Node temp = headNode;
boolean flag = false;
while (true) {
if (temp.next() == null) {
break;
}
if (temp.next().getNo() == no) {
flag = true;
break;
}
temp = temp.next();
}
if (flag) {
temp.setNext(temp.next().next());
} else {
System.out.println("删除成功!");
}
}
// 获取单链表长度(不统计头结点)
public int listLength() {
int length = 0;
Node node = headNode;
while (node.next() != null) {
length++;
node = node.next();
}
return length;
}
// 查找单链表中第k个节点
// (使用的时候index从1开始!)
public Node indexNode( Integer index) {
Node node = headNode.next();
int length = listLength();
if (index <= 0 || index > length) {
return null;
}
for (int i = 0; i < index-1; i++) {
node = node.next();
}
return node;
}
// 查找单链表中倒数第k个节点
public Node lastIndexNode(Integer index) {
Node node = headNode.next();
int length = listLength();
if (index <= 0 || index > length) {
return null;
}
// 倒数第一个从数组的角度考虑其实是第零个
for (int i = 0; i < length - index; i++) {
node = node.next();
}
return node;
}
}
3、对单链表的一些高级操作
1、链表反转
public void reverseList(Node node) {
/* 链表反转:
解法一(利用之前的api):
利用已经写出的获取倒数第n个结点的api,反向生成一个新的链表
Node node = list.headNode.next();
SingleLinkedList result = new SingleLinkedList();
// int length = list.listLength(list);
// for (int i = 1; i <= length; i++) {
// Node node = list.lastIndexNode(list, i);
// Node newNode = new Node(node.getNo(), node.getName(), node.getNickName());
// result.addNode(newNode);
// }
// return result;
解法二(利用之前的api):
利用已经写出的将要插入的结点插入头结点后的api,遍历原链表并依次将结点添加到新链表的头结点后
int length = list.listLength(list);
for (int i = 0; i < length; i++) {
if (node != null) {
result.addToHead(new Node(node.getNo(),node.getName(),node.getNickName()));
}
node = node.next();
}
return result;
}*/
// 解法三(老师解法)
// 当前链表为空或者只有一个节点时,不反转直接返回
if (node.next() == null || node.next().next() == null) {
return;
}
// 定义辅助指针遍历原来的链表
Node cur = node.next();
// 指向当前节点的下一个节点
Node next = null;
Node reverseHead = new Node(0, "", "");
while (cur != null) {
next = cur.next();
cur.setNext(reverseHead.next()); /*将cur的next设置为reverse的next*/
reverseHead.setNext(cur); /*将cur插入到reverse的链表头*/
cur = next;
}
node.setNext(reverseHead.next());
}
2、逆序打印单链表(利用数据结构:栈 )
注意该题目中只是逆序打印单链表,并没有改变单链表的结构
// 逆序打印单链表
public void printReverseLinkedList(SingleLinkedList list) {
Node headNode = list.headNode;
if (headNode.next() == null) {
return; //空链表不能打印
}
// 初始化栈
Stack<Node> stack = new Stack<>();
Node node = headNode.next();
// 链表入栈
while (node != null) {
stack.push(node);
node = node.next();
}
// 链表元素出栈
while (stack.size() > 0) {
System.out.println(stack.pop());
}
}
3、合并有序列表
// 合并两有序链表
public static SingleLinkedList combineOrderdLinkedList(SingleLinkedList s1, SingleLinkedList s2) {
// 初始化结果链表
SingleLinkedList result = new SingleLinkedList();
// 初始化结果链表的头结点及其next
Node node = result.headNode;
Node next = node.next();
// 获取两个链表的长度
int length1 = s1.listLength();
int length2 = s2.listLength();
// 声明辅助变量帮助循环
int i = 1;
int j = 1;
while (true) {
if (i > length1) {
// 如果s1中的所有元素均被添加到了result链表中,就将s2中的剩余所有元素顺序的添加到result中
while (j <= length2) {
result.addNode(new Node(s2.indexNode(j)));
j++;
}
break;
}
if (j > length2) {
// 同理,如果s2中的所有元素均被添加到了result中,那么s1中的剩余全部元素均顺序添加到result中
while (i <= length1) {
result.addNode(new Node(s1.indexNode(i)));
i++;
}
break;
}
// 依次比较s1和s2链表中的元素的no,使结果链表有序
if (s1.indexNode(i).getNo() < s2.indexNode(j).getNo()) {
result.addNode(new Node(s1.indexNode(i)));
i++;
} else if (s1.indexNode(i).getNo() > s2.indexNode(j).getNo()) {
result.addNode(new Node(s2.indexNode(j)));
j++;
}
}
return result;
}
注意:在这个api中又修改了Node类,为其添加了新的Node的构造方法
public Node(Node node){
no = node.no;
name = node.name;
nickName = node.nickName;
}
在做最后这道题的时候,一直不知道怎么处理添加某个结点时,仅仅添加这一个结点,而事实上在结点添加时,总会带着它的next域一起添加进来
通过上面的构造方法,可以做到在添加某个节点时,不添加它的next域,而只是把它的信息赋值到一个新节点上添加到新链表中
至此,单向链表完,用时3天