1、Java实现简单的单链表:
参考文章:https://www.cnblogs.com/whgk/p/6589920.html
数据结构:单链表的实现-Java
数据结构还是很重要的,就算不是那种很牛逼的,但起码得知道基础的东西,这一系列就算是复习一下以前学过的数据结构和填补自己在这一块的知识的空缺。加油。珍惜校园中自由学习的时光。按照链表、栈、队列、排序、数组、树这种顺序来学习数据结构这门课程把。
一、单链表的概念
链表是最基本的数据结构,其存储的你原理图如下图所示:
上面展示的是一个单链表的存储原理图,简单易懂,head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一个节点,就这样一节一节往下面记录,直到最后一个节点,其中的next指向null。链表有很多种,比如单链表,双链表等等。我们就对单链表进行学习,其他的懂了原理其实是一样的。
二、用java实现单链表
2.1、编写一个Node类来充当结点的模型。我们知道,其中有两个属性,1存放数据的data,2存放下一结点的引用。为简单,data和Node都选用public类型。
package linkList;
public class Node {
// 存放数据的变量,int型
public int data;
// 存放结点的变量,默认为null
public Node next;
// 构造方法
public Node(int data) {
this.data = data;
}
}
2.2、单链表的简单操作(增加,删除,获取总长度,链表元素排序,链表遍历)
注意:在增加结点操作前需要将节点连接起来形成一个链表;
public Node head;
public SingleLinkList(int data) { // 构造函数
this.head = new Node(data);
}
public SingleLinkList() { // 构造函数
// this.head = new Node(data);
}
2.2.1、增加结点操作,addNode(Node)
一开始也会想如果什么结点也没有。是不是需要判断插入的是第一个结点的问题,但写完后发现没有必要,是不是第一个结点操作都是一样的,所以通过移动的指针遍历整个链表,找到最后一个结点,往后添加即可。
// 增加结点
public void addNode(int data) {
Node node = new Node(data);
Node cur = this.head;
if(cur == null) {
head = node;
node.next = cur;
cur = head;
}else {
while(cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
2.2.2、插入结点到链表的指定位置。 insertNodeByIndex(int index,Node node)
// 插入结点到指定位置
public void insertNodeByIndex(int i, Node node) {
// 首先需要判断指定的位置是否合法
if(i<0|i>length()) {
System.out.println("插入位置不合理。");
return;
}
int length =1;
Node cur = this.head;
if(i==0) {
head = node;
node.next = cur;
cur = head;
return;
}
else {
if(i == length++) {
node.next = cur.next;
cur.next = node;
return;
}
cur = cur.next;
}
}
注意:需要判断插入的位置在首位还是在中间,首位插入结点的指针指向与中间插入指针指向又区别。
2.2.3、删除指定位置上的结点 delNodeByIndex(int index)
//删除指定位置的结点
public void delNodeByIndex(int i) {
// 判断 i 是否合理
if(i<0|i>length()-1) {
System.out.println("给定的位置不合理。");
return;
}
int length = 0;
Node cur = head;
Node pre = null;
while(cur != null) {
if(i == length++) {
if(i == 0) {
head = cur.next;
return;
} else {
pre.next = cur.next;
return;
}
}else {
pre = cur;
cur =cur.next;
}
}
}
注意:删除结点此处使用了两个指针pre和cur,pre指向当前结点的前一个节点。此外,在链表的首位删除结点与链表的中间删除结点也有区别。
2.2.4、单链表进行选择排序 selectSortNode()
// 通过选择排序对链表进行排序
public void selectSortNode() {
if(length()<1) {
System.out.println("无需排序");
return;
}
// 选择排序
Node cur = head; //第一层遍历使用的移动指针,最处指向头结点,第一个结点用cur表示
while(cur.next != null) { //第一层遍历链表,从第一个结点开始遍历
Node secondCur = cur.next; //第二层遍历使用的移动指针,secondCur指向第二个结点,我们需要用到是第二个结点开始,所以用cur.next
while(secondCur.next != null) { //第二层遍历,从第二个结点开始遍历
if(cur.data > secondCur.data) { //第二层中的所有结点依次与第一次遍历中选定的结点进行比较,
int t = secondCur.data;
secondCur.data = cur.data;
cur.data = t;
}
secondCur = secondCur.next;
}
if(secondCur.next == null) { // 上面的方法无法读取最后一个结点,所以最后一个结点需另外考虑
if(cur.data > secondCur.data) {
int t = secondCur.data;
secondCur.data = cur.data;
cur.data = t;
}
}
cur = cur.next;
}
}
2.2.5、单链表进行插入排序 insertSortNode()
//通过插入排序对链表进行排序
public void insertSortNode() {
if(length()<1) {
System.out.println("无需排序");
return;
}
Node newHead = new Node(0); // 新链表的头结点
Node newCur = newHead; // 新链表的移动
Node cur = head; // 旧链表的移动指针
if(newCur.next == null) { // 将第一个结点直接放入新链表中
Node node = new Node(cur.data);
node.next = newCur.next;
newCur.next = node;
cur = cur.next; // 旧链表的指针移动到第二个结点处
}
while(cur.next != null) {
if(newCur.next == null) { // 先考虑新的链表中只有一个结点时
if(newCur.data > cur.data) {
Node node = new Node(cur.data);
node.next = newCur;
head = node;
}
}else {
while(newCur.next != null) { // 新的链表有多个各结点时
// pre = newCur;
if(newCur.next.data > cur.data) {
Node node = new Node(cur.data);
node.next = newCur.next;
newCur.next = node;
break;
}
newCur = newCur.next;
}
if(newCur.next == null) { // 到达末尾还没找到,该值最小,直接添加到链表。
Node node = new Node(cur.data);
newCur.next = node;
}
}
cur = cur.next; // 旧链表指针指向下一位节点,继续重复和新链表的结点比较
newCur = newHead; // 新链表的移动指针需要复位,指向头结点
}
head = newHead; //开始使用新链表
}
2.2.7、计算单链表的长度
// 链表的长度
public int length() {
int length =0;
Node cur = this.head;
while(cur != null) {
length++;
cur = cur.next;
}
return length;
}
2.2.8、遍历单链表,打印data
// 打印链表中的所有结点的data
public void print() {
Node temp =this.head;
while(temp != null) {
System.out.print(temp.data+" ");
temp = temp.next;
}
System.out.println();
}
三、总结
基本的单链表操作就是上面这些了,自己完全手动写一写,会对单链表这种数据结构的理解有很大的帮助。当然其中比较难的是在于对链表的排序上,说难也不难,只要懂的几种排序的原理,就跟写伪代码一样,去实现即可。这里还有一些操作,大家可以动手做一做,我会在下一篇文章中讲解这些答案。
3.1、如何从链表中删除重复数据
3.2、如何找出单链表中的倒数第k个元素
3.3、如何实现链表的反转
3.4、如何从尾到头输出单链表
3.5、如何寻找单链表的中间结点
3.6、如何检测一个链表是否有环
3.7、如何在不知道头结点的情况下删除指定结点
3.8、如何判断两个链表是否相交
附录:
完整代码包含两部分:封装的Node结点和SingleLinkList的实现
1、Node.java
package linkList;
public class Node {
// 存放数据的变量,int型
public int data;
// 存放结点的变量,默认为null
public Node next;
// 构造方法
public Node(int data) {
this.data = data;
}
}
2、SingleLinkList.java
package linkList;
public class SingleLinkList {
public Node head;
public SingleLinkList(int data) {
this.head = new Node(data);
}
public SingleLinkList() {
// this.head = new Node(data);
}
// 增加结点
public void addNode(int data) {
Node node = new Node(data);
Node cur = this.head;
if(cur == null) {
head = node;
node.next = cur;
cur = head;
}else {
while(cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
// 插入结点到指定位置
public void insertNodeByIndex(int i, Node node) {
// 首先需要判断指定的位置是否合法
if(i<0|i>length()) {
System.out.println("插入位置不合理。");
return;
}
int length =1;
Node cur = this.head;
if(i==0) {
head = node;
node.next = cur;
cur = head;
return;
}
else {
if(i == length++) {
node.next = cur.next;
cur.next = node;
return;
}
cur = cur.next;
}
}
//删除指定位置的结点
public void delNodeByIndex(int i) {
// 判断 i 是否合理
if(i<0|i>length()-1) {
System.out.println("给定的位置不合理。");
return;
}
int length = 0;
Node cur = head;
Node pre = null;
while(cur != null) {
if(i == length++) {
if(i == 0) {
head = cur.next;
return;
} else {
pre.next = cur.next;
return;
}
}else {
pre = cur;
cur =cur.next;
}
}
}
// 通过选择排序对链表进行排序
public void selectSortNode() {
if(length()<1) {
System.out.println("无需排序");
return;
}
// 选择排序
Node cur = head;
while(cur.next != null) {
Node secondCur = cur.next;
while(secondCur.next != null) {
if(cur.data > secondCur.data) {
int t = secondCur.data;
secondCur.data = cur.data;
cur.data = t;
}
secondCur = secondCur.next;
}
if(secondCur.next == null) {
if(cur.data > secondCur.data) {
int t = secondCur.data;
secondCur.data = cur.data;
cur.data = t;
}
}
cur = cur.next;
}
}
//通过插入排序对链表进行排序
public void insertSortNode() {
if(length()<1) {
System.out.println("无需排序");
return;
}
Node newHead = new Node(0); // 新链表的头结点
Node newCur = newHead; // 新链表的移动
Node cur = head; // 旧链表的移动指针
if(newCur.next == null) { // 将第一个结点直接放入新链表中
Node node = new Node(cur.data);
node.next = newCur.next;
newCur.next = node;
cur = cur.next; // 旧链表的指针移动到第二个结点处
}
while(cur.next != null) {
if(newCur.next == null) {
if(newCur.data > cur.data) {
Node node = new Node(cur.data);
node.next = newCur;
head = node;
}
}else {
while(newCur.next != null) {
// pre = newCur;
if(newCur.next.data > cur.data) {
Node node = new Node(cur.data);
node.next = newCur.next;
newCur.next = node;
break;
}
newCur = newCur.next;
}
if(newCur.next == null) { // 到达末尾还没找到,该值最小,直接添加到链表。
Node node = new Node(cur.data);
newCur.next = node;
}
}
cur = cur.next; // 旧链表指针指向下一位节点,继续重复和新链表的结点比较
newCur = newHead; // 新链表的移动指针需要复位,指向头结点
}
head = newHead; //开始使用新链表
}
// 链表的长度
public int length() {
int length =0;
Node cur = this.head;
while(cur != null) {
length++;
cur = cur.next;
}
return length;
}
// 打印链表中的所有结点的data
public void print() {
Node temp =this.head;
while(temp != null) {
System.out.print(temp.data+" ");
temp = temp.next;
}
System.out.println();
}
//main函数
public static void main(String[] args) {
SingleLinkList list = new SingleLinkList();
// Node node1 = new Node(9);
list.addNode(3);
list.addNode(2);
list.addNode(1);
list.addNode(6);
list.addNode(0);
list.print();
list.insertSortNode();
list.print();
// list.delNodeByIndex(0);
// list.print();
// list.insertNodeByIndex(0, node1);
// list.print();
// System.out.println(list.length());
}
}
本人经过实际测试,如有问题欢迎提问。