链表
在刷代码随想录的同时,能够同步完成Java数据结构的相关学习。并且能够尽量自己实现相关数据结构。
书籍《Java数据结构和算法》
基本知识
-
数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。在链表中,如果想要寻找一个特定的元素,唯一方法就是沿着这个链表的链一直向下寻找。要从第一项开始,到第二项、第三项……直到找到需要的那个数据项。
-
Java中,链表也是定义在类中的,一般来说,其模板如下:
public class ListNode { // 结点的值 int val; // 下一个结点 ListNode next; //构造函数以及其他函数这里暂不体现 }- 每个ListNode对象都包含一个对下一个链节点引用的字段,上面代码中是next。这种类定义有时也称为“自引用式”。
-
在链表的实现过程中,根据选择的不同,一般有两种。一种是带有虚拟头结点——即头结点中不存储任何数据,仅用于指向链表;另一种就是头结点实实在在存储了数据,是真实的。
- 个人理解上,感觉虚拟头结点更为方便。
-
以下代码实现,为保持和书籍一致,采用的是后者,真实的头结点。
单链表
单链表具体代码实现
//linkNode类
class linkNode {
public int iData;
public double dData;
public linkNode next;
public linkNode(){
}
public linkNode(int iData,double dData){
this.iData=iData;
this.dData=dData;
}
public void displayLinknode(){
System.out.println("iData="+this.iData+",dData"+this.dData);
}
@Override
public String toString() {
return "linkNode{" +
"iData=" + iData +
", dData=" + dData +
", next=" + next +
'}';
}
}
代码分割线
//linkList类
//按照书上说法,因为一个链表中有许多类似的链节点,所以有必要用一个不同于链表的类来表示链结点。
//个人理解就是listNode类仅用于表示结点,而链表(包括头结点)应该用一个额外的类来存储。
//(其实还挺有道理的,有点符合面向对象编程那种感觉)
import java.util.ArrayList;
public class linkList {
private linkNode first;
public linkList(){
}
public boolean isEmpty(){
//first==null的话 return true 说明当前链表是空的 否则就非空
return (first==null);
}
/**
*
* @param iData 新插入节点的iData数据
* @param dData 新插入节点的dData数据
* @param insertIndex 节点插入到第几个节点 如insertIndex=1 表示插入到第一个节点,即变成头结点。=2 表示插入到第二个节点
* @return true表示插入成功 false表示插入失败
*/
public boolean insertNodes(int iData,double dData,int insertIndex){
linkNode insertNode=new linkNode(iData,dData);
//如果当前链表都是空的
if(this.isEmpty()){
this.first=insertNode;
first.next=null;
return true;
}
else{
//如果要插入的位置都大于了我们的这个链表的长度,那么就插入到最后
if(insertIndex>this.getLinkListLength()){
linkNode temp=first;
System.out.println("被插入位置已大于当前链表长度,将其插入称为尾节点。");
while(temp.next!=null){
temp=temp.next;
}
temp.next=insertNode;
return true;
}
//如果插入的位置小于等于1
else if(insertIndex<=1){
//插入到头结点
System.out.println("被插入位置已<=1,插入到头结点");
insertNode.next=first;
first=insertNode;
return true;
}
else{
//如果要插入的位置我们这个链表的长度之间
linkNode pre =first;
int count=1; //因为不是虚拟头结点,所以头结点要算入 count=1
while(count!=insertIndex-1){
pre=pre.next;
}
insertNode.next=pre.next;
pre.next=insertNode;
return true;
}
}
}
/**
* 时间有限,就暂时完成删除所有this.iData=iData的节点
* @param iData
* @return ArrayList<linkNode> 因为可能iData可能有重复的,即可能有大量节点被删除
*/
public ArrayList<linkNode> deleNodes(int iData){
ArrayList<linkNode> deletedNodes=new ArrayList<>();//存储被删除的节点
//如果链表为空,就直接返回null ,并sout链表为空
if(this.isEmpty()){
System.out.println("链表为空");
return null;
}
linkNode pre=first;
linkNode temp=pre.next;
//因为我想一次性遍历完,所以还是暂时先从first的next开始。
//但是这样就避开了first的判断,所以循环结束还要把first再判断一次
while(temp!=null){
if(temp.iData==iData){
deletedNodes.add(temp);
pre.next=temp.next;
temp=pre.next;
}
else{
pre=temp;
temp=pre.next;
}
}
//这里就是 first没有判断过,所以要判断一下
if(first.iData==iData){
deletedNodes.add(first);
first=first.next;
}
return deletedNodes;
}
public int getLinkListLength(){
if (isEmpty()){
return 0;
}
linkNode temp=first;
int length=1;
while(temp.next!=null){
temp=temp.next;
length++;
}
return length;
}
public void displayWholeLinklist(){
linkNode temp=first;
if(isEmpty()){
System.out.println("当前链表为空");
return;
}
while(temp!=null){
temp.displayLinknode();
temp=temp.next;
}
}
}
//个人测试用
import java.util.ArrayList;
public class testLinklist {
public static void main(String[] args) {
linkList first=new linkList();
first.insertNodes(4,1.5,1);
first.insertNodes(4,2.5,1);
first.insertNodes(4,2.5,2);
first.insertNodes(4,4.5,4);
first.insertNodes(4,4.5,10);
first.displayWholeLinklist();
ArrayList<linkNode> deletedNodes=first.deleNodes(4);
System.out.println("被删除的节点为:"+deletedNodes.toString());
first.displayWholeLinklist();
}
}