数据结构(一):链表(Linked List)
1.链表是以结点的方式来存储的;
2.每个结点包含①data域:存储数据 ②next域:指向下一个结点
3.链表的各个结点在内存中不一定是连续存储的,但是它存储的内容是有序的(按照你添加结点的顺序)
4.链表包块带头结点和不带头结点两种
结点形式如下:
class Node{
int data;
Node next = null;
public Node(int data){
this.data = data;
}
}
链表在内存中存储形式(物理结构)如下:
单链表(带头结点)逻辑结构图如下:
java:单链表(不带头结点)的实现
package com.study;
//节点
class Node {
int data;
Node next = null;
public Node(int data) {
this.data = data;
}
}
//单链表
class MyLinkList {
Node head = null;//链表头引用:作用等同于头指针
/*
* 尾插法:单链表中插入节点,在末尾位置插入
* */
public void addNodeTail(int data) {
Node newNode = new Node(data);//传入数据,实例化一个节点,用于添加。
if (head == null) {//头结点指向为空,说明该链表为空
head = newNode;
return;
}
Node temp = head;//作用等同于指针
while (temp.next != null) {
temp = temp.next;
}
temp.next = newNode;//将新节点加入到链表
}
/*
* 头插法:单链表插入结点,在表头插入
* */
public void addNodeHead(int data) {
Node newNode = new Node(data);
if (head == null) {//链表为空
head = newNode;
return;
}
Node temp = head;
newNode.next = temp;
head = newNode;
}
/*
* @param index:删除第index个节点
* */
public boolean delete(int index) {
if (index < 1 || index > length()) {//避免了当index=0且链表为空的情况;避免了输入index超出节点数;验证了index不能为负数;
return false;
}
while (index == 1) {//不带头结点的单链表删除第一个结点的情况,要特殊处理
head = head.next;
return true;
}
Node preNode = head;
Node curNode = preNode.next;
int i = 1;
while (curNode != null) {
if (i == index) {
preNode.next = curNode.next;
return true;
}
preNode = curNode;
curNode = preNode.next;
i++;
}
return false;
}
/*
* @return:返回节点的长度
* */
public int length() {
int length = 0;
Node temp = head;
if (temp != null) {
length++;
temp = temp.next;
}
return length;
}
/*
* 遍历单链表
* */
public void printLinkList() {
Node temp = head;
if(temp==null){
System.out.println("链表为空");
}
while (temp != null) {
System.out.println(temp.data);
temp = temp.next;
}
}
}
public class LinkListDemo {
public static void main(String[] args) {
MyLinkList myLinkList = new MyLinkList();
myLinkList.addNodeHead(1);//头插法
myLinkList.printLinkList();//打印
myLinkList.addNodeTail(10);//尾插法
myLinkList.printLinkList();//打印
myLinkList.delete(1);//删除第一个节点
myLinkList.printLinkList();//d打印
System.out.println(myLinkList.length());//长度
}
}
打印结果:
java:单链表(带头结点)的实现
package com.study;
//节点
class Node {
int data;
Node next = null;
public Node(int data) {
this.data = data;
}
}
//单链表
class MyLinkList2{
Node head = new Node(0);//头结点,数据域存储什么无所谓
/*
* 头插法
* */
public void addNodeHead(int data){
Node newNode = new Node(data);
Node temp = head;
newNode.next = temp.next;
temp.next = newNode;//这里我们和上面不带头结点的头插法比较,发现此处根本不需要考虑链表是否为空的情况。不管是否为空,写法都一样。
}
/*
* 尾插法
* */
public void addNodeTail(int data){
Node newNode = new Node(data);
Node temp = head;
while(temp.next != null){
temp = temp.next;
}
temp.next = newNode;
}
/*
* 删除第index个结点
* */
public boolean delete(int index){
if(index < 0 || index > length()){
return false;
}
Node temp = head;
Node preNode = temp;
Node curNode = preNode.next;
int i = 1;
while(temp.next != null){
if(i == index){
preNode.next = curNode.next;
break;
}
preNode = curNode;
curNode = preNode.next;
i++;
}
return true;
}
/*
* @return:单链表长度
* */
public int length(){
Node temp = head;
int length = 0;
while(temp.next != null){
length++;
temp = temp.next;
}
return length;
}
/*
* 遍历单链表
* */
public void printLinkList(){
Node temp = head;
if(temp.next==null){
System.out.println("链表为空");
}
while(temp.next != null){
temp = temp.next;
System.out.println(temp.data);
}
}
}
//===================开始测试========================================
public class LinkListDemo2 {
public static void main(String[] args) {
MyLinkList2 myLinkList = new MyLinkList2();
myLinkList.addNodeHead(1);//头插法
myLinkList.printLinkList();//打印
myLinkList.addNodeTail(10);//尾插法
myLinkList.printLinkList();//打印
myLinkList.delete(1);//删除第一个节点
myLinkList.printLinkList();//d打印
System.out.println(myLinkList.length());//长度
}
}
打印结果:
链表结点的删除讲解(后面截图里面的说明有点小问题,以下面的文字说明为准)
我们现在以删除第二个结点,(称为结点b)为例子,方法:先连后断。
步骤①:先用一个指针p指向结点a;
步骤②:再用指针q指向结点c;
步骤③:然后令p.next =q;
①②两步就是“连”,③就是“断”。
有人会问,为啥不直接head.next = head.next.next;这样的确是可以,但是假如我现在要删除的是结点m,那么我们怎么写??这个时候,我么那就没法知道要next多少次了。所以必须要有个指针p,利用while循环,用来替代head的作用,同时也是因为head是指向头结点的,不能随意变动它。