链表的基本结构
public class LNode {
public String data;//节点数据
LNode next;//下一个节点
}
这是最基本的链表结构,还有双向链表和循环链表会在文章最后提到。
建立单链表
头部插入建立单链表
头部插入建立方式的思想是,首先建立一个指针,
1.然后新建第一个节点,让指针指向这个节点
2.然后再新建一个节点,通过指针拿到第一个点的地址,让新建的下一个地址指向第一个节点。
3.最后把指针移动到第一个节点上。
后面重复这个过程就可以

大家可以根据这张图来理解一下。
下面是实现的代码
// 在头部插入
public LNode Creat_LinkListHead() {
LNode head = null;// 头部,空表
System.out.println("现在使用头部插入法,请输入数据,以0为结束");
String newDate;
while (!(newDate = in.next()).equals("0")) {
LNode newNode = new LNode();//新建一个节点
newNode.data = newDate;
newNode.next = head;//通过指针找到上一个节点地址,让新节点的下一个地址指向新节点
head = newNode;//让指针移动到新建的节点上
}
System.out.println("录入完毕");
return head;
}
在尾部插入建立链表
在头部插入的方式,有一个很大的问题,就是最后我指针的位置,在整个链表逻辑上 的最后位,遍历的时候,会反过来,比方说建立的顺序是ABCDE,遍历的时候就是EDCBA了
在尾部插入建立的方式就是为解决这个问题
尾部插入的思路是:首先我们建立一个头节点和一个尾节点,让头节点和尾节点一起指向第一个节点,之后的话头结点不再变动,尾节点用头部插入的方式建立起链表,可以发现这个方式和头部插入没有什么区别,主要就是增加了一个头节点。
下面是实现的代码
// 在尾部插入
public LNode Creat_LinkListTail() {
LNode H = null;// 头指针
LNode R = null;// 尾指针
String newDate;
System.out.println("现在使用尾部插入法,以0为结束");
while (!(newDate = in.next()).equals("0")) {
LNode newNode = new LNode();
newNode.data = newDate;
// 第一个指针
if (H == null) {
H = newNode;
} else {
R.next = newNode;
}
//指针移动到创建的新节点
R = newNode;
}
//让随后一个节点指向null
if (R != null)
R.next = null;
return H;
}
带头节点的尾部插入
上面方式也有一个问题,第一个节点由于没有前驱,所以我们在很多操作的时候都要考虑这个特殊的点,这样在增加删除的时候都会比较麻烦,比较好的解决方式,我们可以第一个节点前面在加一个节点,这个节点没有数据,只是指向原来的第一个节点。如下图

H代表的是头指针
R代表的是尾指针
新增一个头结点后就可以很好的解决这个问题,但在遍历,定位节点的时候要注意哥不带头结点的链表的区别,这个头结点 只是用来作为前驱存放原本第一个节点的地址的。
public LNode Creat_LinkListTailWithHead() {
LNode H = null;//头指针
LNode R = null;//尾指针
//建立头结点
LNode headNode = new LNode();
headNode.next = null;
H = headNode;
R = headNode;
System.out.println("现在使用带头结点的的链表尾部插入,输入0为结束");
String newDate;
while(!(newDate=in.next()).equals("0")){
LNode newNode = new LNode();
newNode.data = newDate;
newNode.next = null;
R.next = newNode;
R=newNode;
}
R.next = null;
return H;
}
遍历链表
遍历的思路是从头结点开始,输出节点的数据, 之后让指针移动到下一个节点
//遍历链表
public void look(LNode head) {
if (head == null) {
System.out.println("链表为空");
System.exit(0);
}
LNode flag = head;//用一个指针来拿到头结点的地址
do {
System.out.print(flag.data);
flag = flag.next;//下移到下一个节点
} while (flag != null);//如果节点为空结束循环
}
当然这个遍历是有问题的,没有判断链表有没有带头结点,大家可以自行修改一下。
计算链表长度
这个的思路的遍历差不多,只不过我们需要定义一个变量用来存储长度
//计算带头结点的链表的长度
public int lenth_haveHead(LNode H){
int len = 0 ;//用来存放长度
while(H.next!=null){
H=H.next;
len++;
}
return len;
}
//不带头结点的链表的长度
public int lenth_nothaveHead(LNode H){
int len = 0;//用来存放长度
//判断链表是否为空
if(H==null){
System.out.println("链表为空");
return 0;
}
len = 1;
while(H.next!=null){
H = H.next;
len++;
}
return len;
}
可以看出,带头节点的链表在遍历上就比不带的方便很多。
查询指定节点
//查询指定节点
//n 代表第几个节点
public LNode findLNode(int n,LNode H){
LNode myH;
if(H.data == null){
//带头结点
myH = H.next;
}else{
//不带头结点
myH = H;
}
int len = 0;
while(myH!=null){
len++;
if(len == n) break;
myH = myH.next;
}
return myH;
}
首先我们知道带头节点的链表的第一个节点,没有数据,用这个作为标志来判断链表是哪种链表。
如果是带头结点的,那链表的第一位应该是头结点.next
后排插入
后排插入就是在一个节点的后面,插入一个新节点
思路如下
我们先找到这个节点
通过这个节点的next得到下一个节点的地址,让新节点的next指向下一个节点。
让被插的节点将next指向新的节点

下面给出实现代码
//在第n的节点后插入data
public void insertBehind(String data,int n,LNode H){
MyLinkList list = new MyLinkList();
LNode newNode = new LNode();//新建节点
newNode.data = data;
newNode.next = null;
LNode node = list.findLNode(n, H);//找到要插入的位置
if(node == null) {
System.out.println("找不到节点");
return;
}
//获取前驱的next,也就是下一个节点
newNode.next = node.next;
//让前驱指向新的节点
node.next = newNode;
}
前排插入
前排插入的思想和后排插入差不多,只不过我们再拿到节点后,需要再去查找一次,找到这的节点的前驱,之后以这个前驱做后排插入就行了。
/*
* 在第n个节点前面插入一个节点
* */
public void insertFront(String data,int n,LNode H){
LNode newNode = new LNode();
newNode.data = data;
newNode.next = null;
MyLinkList list = new MyLinkList();
//找到要插入的节点
LNode node = list.findLNode(n, H);
if(node==null){
System.out.println("找不到节点");
return;
}
//找到node节点的前驱
LNode myH = H;
while(myH.next!=node){
myH = myH.next;
}
newNode.next = node;
myH.next = newNode;
}
删除节点
删除节点的思路如下:
找到要删除的节点
再找到他的前驱节点
让前驱的next指向这个节点的next,这个时候要删除的节点已经被架空了
最后我们给这个要删除的节点赋值null 释放空间

MyLinkList list = new MyLinkList();
LNode node = list.findLNode(n, H);
if(node == null) {
System.out.println("找不到要删除的节点");
return;
}
//找到这个节点的前驱
LNode frontNode = list.findLNode(n-1, H);
if(frontNode==null){
System.out.println("找不到前驱");
return;
}
frontNode.next = node.next;
node = null;
}
循环链表
循环链表就是将最后一个节点,原来指向空,改成指向头部,实质上并没有什么区别,主要是判断是不能用null来做链表结束的条件了
双向链表
单向的连接再很多时候会有麻烦,比如我们的前排插入,我们要得到前驱,不得不再做一次遍历,双向链表正是为了解决这个问题,我们再结构中再加一个指针让他指向前驱就可以了。
package com.ys.datastructure;
public class TwoWayLinkedList {
private Node head;//表示链表头
private Node tail;//表示链表尾
private int size;//表示链表的节点个数
private class Node{
private Object data;
private Node next;
private Node prev;
public Node(Object data){
this.data = data;
}
}
public TwoWayLinkedList(){
size = 0;
head = null;
tail = null;
}
//在链表头增加节点
public void addHead(Object value){
Node newNode = new Node(value);
if(size == 0){
head = newNode;
tail = newNode;
size++;
}else{
head.prev = newNode;
newNode.next = head;
head = newNode;
size++;
}
}
//在链表尾增加节点
public void addTail(Object value){
Node newNode = new Node(value);
if(size == 0){
head = newNode;
tail = newNode;
size++;
}else{
newNode.prev = tail;
tail.next = newNode;
tail = newNode;
size++;
}
}
//删除链表头
public Node deleteHead(){
Node temp = head;
if(size != 0){
head = head.next;
head.prev = null;
size--;
}
return temp;
}
//删除链表尾
public Node deleteTail(){
Node temp = tail;
if(size != 0){
tail = tail.prev;
tail.next = null;
size--;
}
return temp;
}
//获得链表的节点个数
public int getSize(){
return size;
}
//判断链表是否为空
public boolean isEmpty(){
return (size == 0);
}
//显示节点信息
public void display(){
if(size >0){
Node node = head;
int tempSize = size;
if(tempSize == 1){//当前链表只有一个节点
System.out.println("["+node.data+"]");
return;
}
while(tempSize>0){
if(node.equals(head)){
System.out.print("["+node.data+"->");
}else if(node.next == null){
System.out.print(node.data+"]");
}else{
System.out.print(node.data+"->");
}
node = node.next;
tempSize--;
}
System.out.println();
}else{//如果链表一个节点都没有,直接打印[]
System.out.println("[]");
}
}
}
文章来源:
https://www.cnblogs.com/ysocean/p/7928988.html
https://blog.youkuaiyun.com/zjsxxzh/article/details/75102058
本文详细介绍了链表的基本结构及各种操作方法,包括单链表的头部和尾部插入、遍历、查询、插入和删除节点等,并对比了带头结点与不带头结点的链表在操作上的差异。
530

被折叠的 条评论
为什么被折叠?



