单链表
概念
首先对单链表的结构进行介绍,单链表是由很多个节点一个接一个串接起来的,每个节点包含两部分,数据部分和地址部分,我们这里讲的是无头的单链表,所以没有头节点,每个节点存储的是本身的数据和下一个节点的地址。
如图,每个节点对应下面的地址。那第一个节点的地址设置为0x111,节点中存储的数据为1,存储的下一个节点的地址为0x22,直到最后一个节点因为后面没有节点了所以存储的地址为null(空)。
对于单链表的操作具体有以下一些步骤,(单链表是一种类型,也是由一个一个的节点构成,所以我们在进行所有的操作的时候,首先定义一个节点类,这类呢要包括图上所示的数据、next两个属性,注:next保存地址,所以是一个引用)
== 定义的Node模板如下,整个单链表都是由一个一个的Node节点串接在一起==
刚开始的单链表是没有任何数据的,就像是一张白纸什么也没有,也没有地址,所以随用随插。
操作
1、 单链表的打印
首先定义一个引用类型cur,使其等于head,这时cur就指向了第一个节点,那cur=cur.next是把第一个节点中存储的第二个节点的地址赋给cur,以此类推直到cur为空,打印完最后一个节点。
拿上图举例。第一个节点的地址为0x111,head指向第一个节点的地址,使cur=head后cur中就是0x111第一个节点的地址,cur.next此时为0x222就是第二个节点的地址,cur=cur.next,就是将第二个节点的地址放入cur,此时cur中为0x222,以此类推直到cur中为空。
public void disPlay(){
Node cur = this.head;
while(cur!=null){
System.out.print(cur.getData()+" ");
cur = cur.getNext();
}
}
2、头插法
头插法,首先利用构造方法new一个新的节点,之后呢将原来的第一个定义为head的节点的地址放到刚刚新的节点的next中,然后 使原来指向第一个节点的head,现在指向新的第一个节点。
由上图的例子,新建一个节点node地址为0x456,里面的数据为5,然后将原来的第一个节点的地址0x111放入刚刚的新节点中(即node.next=0x111),这样就可以让现在的新节点指向原来的第一个节点,之后让原来指向第一个节点的head现在指向新的地址为0x456的节点,这样就实现了头插法。
当然上述情况是在单链表本来就有节点的情况下,那样就可以直接在第一个节点之前插入,但是如果一开始单链表为空的话,那就要添加判断条件让head直接指向我们新new的节点,这样我们所new的节点就直接为第一个节点。
public void addFirst(int data){
Node node = new Node(data);
if(this.head == null){
//单链表中无节点
this.head = node;
return;
}
node.setNext(this.head);
this.head = node;
}
3、尾插法
尾插法,首先 new一个新的节点,之后呢定义一个引用cur,让cur指向head,开始一直遍历到最后直到cur.next为空,这个时候cur指向的就是最后一个节点然后 将新new的节点地址放入到cur之中,这样新的节点就成为新的尾节点。
public void addLast(int data){
Node node = new Node(data);
if(this.head == null){
//单链表中无节点
this.head = node;
return;
}
Node cur = this.head;
while(cur.getNext()!=null){
cur = cur.getNext();
}
//此时cur指向最后一个节点
cur.setNext(node);
}
4、在任意位置插入,第一个数据节点为0号下标
首先在插入的时候进行判断,判断index是否合法,之后如果index为0,则直接进行头插,如果 index等于单链表长度则直接进行尾插。
== 设置一个方法得到prev,prev指向index前一个节点的地址。==
注意:这里一定要如图先将index的地址放入新节点中,之后再将新节点的地址放入原index的前一个地址,如果顺序错误先进行3步骤则会丢失后面的数据。
下图中应为findIndex(index)
//在任意位置插入,第一个数据节点为0号下标
public Node findIndex(int index){//找到prev
int count = 0;
Node cur = this.head;
while (count < index-1){
cur.setNext(cur.getNext());
count++;
}
return cur;
}
public void addIndex(int index,int data){
if(index<0||index>size()){
throw new RuntimeException("输入位置不合法");
}else if(index == 0){
addFirst(data);
}else if(index == size()){
addLast(data);
}else {
Node node = new Node(data);
Node prev = findIndex(index);
node.setNext(prev.getNext());
prev.setNext(node);
}
}
public int size(){
int count = 0;
Node cur = this.head;
while (cur!=null){
count++;
cur = cur.getNext();
}
return count;
}
5、查找是否包含关键字key是否在单链表当中
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
Node cur = this.head;
while (cur!=null){
if(cur.getData() == key){
return true;
}
cur = cur.getNext();
}
return false;
}
6、删除第一次出现关键字为key的节点
删除关键字时首先要判断单链表之中是否存在这个关键字,其次判断这个关键字是否在第一个节点。之后按照常规的操作,先求出要删除的关键字的前驱
如下图中如果要删除2关键字就要求得存储2这个节点的前驱,将2这个节点的后继地址放入2的前驱的地址域就删除了2这个关键字节点。(这个地方存储1的节点不是第一个节点)
//删除第一次出现关键字为key的节点
private Node findPrev(int key){
Node prev = this.head;
while(prev.getNext()!=null){
if(prev.getNext().getData() == key){
return prev;
}
prev = prev.getNext();
}
return null;
}
public void remove(int key){
if(this.head.getData() == key){
this.head = this.head.getNext();
return;
}
Node prev = findPrev(key);
if(prev == null){
System.out.println("没有这个节点!");
return;
}
Node vel = prev.getNext();
prev.setNext(vel.getNext());
}
7、删除所有值为key的节点
一次性删除所有关键字key,首先定义两个引用prev和cur,cur为prev的后继,prev指向第一个节点,由下图举例,删除里面的所有的2,定义好两个引用之后,如果cur指向的节点的数值=2,则将cur向右移动即cur=cur.next,直到cur指向节点的数值不为2,如下图此时将值为4的节点地址放入prev的地址域,prev.next=cur,之后将prev移到cur位置,然后cur继续进行判断直到cur为空。当然如果第一个节点也需要删除的话只需添加判断条件之后将head指向下一个节点。
//删除所有值为key的节点
public void removeAllKey(int key){
Node prev = this.head;
Node cur = prev.getNext();
while(cur!=null){
if(cur.getData() == key){
prev.setNext(cur.getNext());
}else{
prev = cur;
}
cur = cur.getNext();
}
if(this.head.getData() == key){
this.head = this.head.getNext();
}
}
8、清空单链表
单链表的清空很简单,因为在单链表上储存的是对象。所以会存在内存泄漏,所以在不引用之后要对内存进行回收。
第一种:只要将指向第一个节点的head置空,这样就没有被引用的节点。
第二种:就是一个一个进行释放。
先利用循环将第一个节点之后的所有节点删除,定义del指向head.next,之后将dei.next放入head的next域之中,不断循环直到head的next域为空此时再最后删除第一个节点。
最后上代码!!!
class Node{
private int data;
private Node next; //利用引用类型储存地址,因为已经定义Node所以可以直接引用
public Node(int data){
this.data = data;
this.next = null;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
//组成单链表
public class MyLineList {
public Node head; //定义一个头节点
public MyLineList(){
this.head = null; //设置头节点为空,也可以不设置
}
//打印单链表
public void disPlay(){
Node cur = this.head;
while(cur!=null){
System.out.print(cur.getData()+" ");
cur = cur.getNext();
}
}
//头插法
public void addFirst(int data){
Node node = new Node(data);
if(this.head == null){
//单链表中无节点
this.head = node;
return;
}
node.setNext(this.head);
this.head = node;
}
//尾插法
public void addLast(int data){
Node node = new Node(data);
if(this.head == null){
//单链表中无节点
this.head = node;
return;
}
Node cur = this.head;
while(cur.getNext()!=null){
cur = cur.getNext();
}
//此时cur指向最后一个节点
cur.setNext(node);
}
//在任意位置插入,第一个数据节点为0号下标
public Node findIndex(int index){//找到prev
int count = 0;
Node cur = this.head;
while (count < index-1){
cur.setNext(cur.getNext());
count++;
}
return cur;
}
public void addIndex(int index,int data){
if(index<0||index>size()){
throw new RuntimeException("输入位置不合法");
}else if(index == 0){
addFirst(data);
}else if(index == size()){
addLast(data);
}else {
Node node = new Node(data);
Node prev = findIndex(data);
node.setNext(prev.getNext());
prev.setNext(node);
}
}
public int size(){
int count = 0;
Node cur = this.head;
while (cur!=null){
count++;
cur = cur.getNext();
}
return count;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
Node cur = this.head;
while (cur!=null){
if(cur.getData() == key){
return true;
}
cur = cur.getNext();
}
return false;
}
//删除第一次出现关键字为key的节点
private Node findPrev(int key){
Node prev = this.head;
while(prev.getNext()!=null){
if(prev.getNext().getData() == key){
return prev;
}
prev = prev.getNext();
}
return null;
}
public void remove(int key){
if(this.head.getData() == key){
this.head = this.head.getNext();
return;
}
Node prev = findPrev(key);
if(prev == null){
System.out.println("没有这个节点!");
return;
}
Node vel = prev.getNext();
prev.setNext(vel.getNext());
}
//删除所有值为key的节点
public void removeAllKey(int key){
Node prev = this.head;
Node cur = prev.getNext();
while(cur!=null){
if(cur.getData() == key){
prev.setNext(cur.getNext());
}else{
prev = cur;
}
cur = cur.getNext();
}
if(this.head.getData() == key){
this.head = this.head.getNext();
}
}
//清空单链表
public void clear(){
this.head = null;/*
while(this.head.getNext()!=null){
Node del = this.head.getNext();
this.head.setNext(del.getNext());
}
this.head = null;*/
}
}
class Demo{
public static void main(String[] args) {
MyLineList myLineList = new MyLineList();
myLineList.addLast(1);
myLineList.addLast(2);
myLineList.addLast(3);
myLineList.addLast(3);
myLineList.addLast(3);
myLineList.addLast(6);
myLineList.addLast(7);
myLineList.addFirst(10);
myLineList.addIndex(2,99);
System.out.println("三种方法插入后结果:");
myLineList.disPlay();
System.out.println();
System.out.println("查找是否存在10这个数据");
System.out.println(myLineList.contains(10));
System.out.println("删除单链表里面的数据10之后:");
myLineList.remove(10);
myLineList.disPlay();
System.out.println();
System.out.println("删除单链表里面所有的3之后:");
myLineList.removeAllKey(3);
myLineList.disPlay();
System.out.println();
System.out.println("清空单链表之后打印:");
myLineList.clear();
System.out.println();
myLineList.disPlay();
}
}
测试:
class Demo{
public static void main(String[] args) {
MyLineList myLineList = new MyLineList();
myLineList.addLast(1);
myLineList.addLast(2);
myLineList.addLast(3);
myLineList.addLast(3);
myLineList.addLast(3);
myLineList.addLast(6);
myLineList.addLast(7);
myLineList.addFirst(10);
myLineList.addIndex(2,99);
System.out.println("三种方法插入后结果:");
myLineList.disPlay();
System.out.println();
System.out.println("单链表长度:");
System.out.println(myLineList.size());
System.out.println("查找是否存在10这个数据");
System.out.println(myLineList.contains(10));
System.out.println("删除单链表里面的数据10之后:");
myLineList.remove(10);
myLineList.disPlay();
System.out.println();
System.out.println("删除单链表里面所有的3之后:");
myLineList.removeAllKey(3);
myLineList.disPlay();
System.out.println();
System.out.println("清空单链表之后打印:");
myLineList.clear();
System.out.println();
myLineList.disPlay();
}
}
计算单链表长度:
结果:
三种方法插入后结果:
10 99 1 2 3 3 3 6 7
单链表长度:
9
查找是否存在10这个数据
true
删除单链表里面的数据10之后:
99 1 2 3 3 3 6 7
删除单链表里面所有的3之后:
99 1 2 6 7
清空单链表之后打印: