单链表

1.单链表

下一个节点的引用即是存放下个节点的地址 以便于连成一个链表

初始化属性

提供一个带有data next 的构造方法 以便于后面使用

class Node{
    public int data;
    public Node next;
//Node为引用类型 head存放头部地址
    public Node head;
    public Node(int data){

         //初始data默认为data 因为data是传入的
        this.data=data;

        //next为一个引用 因此须默认为null 以便于后面使用
        this.next=null;
    }

一  头插法 

把一个节点插入无头单向非循环单链表的头部 并且让head指向这个节点 


    public void addFirst(int data){

        //1.先生成一个节点
        // 把这个生成的节点赋值为data
        Node node=new Node(data);

         if(this.head==null){//证明第一次插入节点
            this.head=node;
             return ;
         }

        //2.把null改为插入到最前面第一个节点的地址 它存在head中
        node.next=this.head;

        //3.更改头部地址 跟node.next 地址不一样 自己想想
            head指向新头部节点       node存的是当前节点地址
        this.head=node;
    }

若插入有头 插到头节点与第二个节点中间

 

步骤总结:

1.Node node=new Node(data);

node是new之后生成的对象

node这个对象可以通过调用属性来方便后面调用 把data赋给数值域

2.node.next=this.head; 

next改为第一个节点的地址 则它成为新头节点

3.this.head=node;

node其实就是保存了新节点的地址

应当赋值让head指向这个新头节点 head保存头节点的地址

注意:

如果是第一次插入节点 那么head为null

则须使head保存这第一个节点的地址

this.head=node; 让head指向node 指向新节点

易错点 * 

 不可写成

1.Node node=new Node(data);

2.this.head=node;

3.node.next=this.head;

会导致节点自己指向自己 直接报废

分析如下

1.表示new一个新对象 方便调用

2.node存放的是当前节点的地址 则第二步地址head指向这个尚未插入的新节点

3.node调用next属性并且赋值head head指向新节点自身 即存新节点自身的地址

那么next 最后赋成了自身的地址

恭喜 直接报废 重开

还有一个重要的知识记录一下

Node是一个类

它的属性有data next 相当于一个模板 根据这个模板产生无数个对象

如对象node可以调用属性 

再复习一遍 可能不完整但是核心

Node node=new Node(data);

if(this.head==null){

this.head=node;

return ;

}

node.next=head;

this.head=node;

每new一次 它都可以产生一个新节点 

二 打印单链表

 重新定义一个cur 如果用head会导致到后面移了之后找不到了

代码如下

 public void display(){
        Node cur=this.head;
        while(cur!=null){
            System.out.println(cur.data);
            cur=cur.next;
        }

分析如下:

1.Node cur=this.head;

重新找一个对象为cur  并且赋予head 由于head存放头节点的地址

所以cur指向头节点

2.while(cur!=null) {

打印cur.data数据 通过cur来调用

cur=cur.next; 前面说过next中存放的都是下一个节点的地址 因此cur赋值后可指向下一个节点

当没有下一个节点时 即为cur.next==null  赋值之后cur等于null

为了能打印最后一个节点 则条件为cur!=null 若为cur.next!=0 最后一个节点data不会打印

三 尾插法

把最后一个节点的null变为要插入节点的地址 如图是777 

Node cur=this.head;

让一个cur指向头节点 然后让cur往后走 当cur.next==null时证明ok了

此时把 cur.next=node; 因为node中存着尾插节点的地址

 实现代码如下

最重要的一点是 通过第一步创建新的节点 记住了 跟头插的时候一模一样
      Node node=new Node(data);

public void addLast(int data){
        //传入一个数据data 
        //最重要的一点是 通过第一步创建新的节点 记住了 跟头插的时候一模一样
        Node node=new Node(data);
        //cur指向头节点
        Node cur=this.head;
        while(cur.next!=null){
            cur=cur.next;
        }
        cur.next=node;
    }

四 查找key是否在单链表中

思想与前面类似 代码如下

public boolean contains(int key){

       //当然第一步可省略new一个node 直接第二步开始 但习惯了还是写上去了
        Node node=new Node(data);//由于前面用构造方法实现了data 所以这里可直接用

        Node cur=this.head;//同理 前面构造方法也实现了head 这里也可以直接用
        while(cur!=null){
            //找到了
            if(cur.data==key){
                return true;
            }
            //没找到 继续往后
            cur=cur.next;
        }
        return false;
    }

五 得到单链表的长度

 public int size(){
        Node cur=this.head;
        int count=0;
        while(cur!=null){
            count++;
            cur=cur.next;
        }
        return count;
    }

this总结

 this可以调用属性或方法 养成习惯就好了

this不可以访问局部变量

如  这是错误的 a不是对象 a在栈里

 

 这是正确的 a定义在外面 this可以访问引用了

六  在index(任意位置)插入一个数据(data)

 你要插到2号位 那么就插到2号位的前面即可 注意是从0号位开始的

最终的目的就是把其串起来

代码如下

//  表示要在下标为index处 插入数据为data的节点
public void addIndex(int index,int data){
    Node node=new Node(data);
        //当index为0时 相当于头插
        if(index==0){
            addFirst(data);
            return ;
        }
        //当index等于长度时 等价于尾插
        if(index==this.size()){
            addLast(data);
            return ;
    }

        //先找到index位前面一个节点的地址
        Node cur=searchIndex(index);

        //找到之后进行插入

       node.next=cur.next;
       cur.next=node;

        private Node searchIndex(int index){
      //先堆index检查是否合法
        if(index<0||index>this.size()){
            throw new RuntimeException("index位置不合法");
        }
        Node cur=this.head;
        while(index-1!=0){
            cur=cur.next;
            index--;
        }
        return cur;
    }
}

七 删除第一次出现关键字为key的节点

代码如下

 private Node searchprev(int key) {
        Node prev = this.head;
        while (prev.next != null) {
            //如果下一个数据等于key时 返回prev
            if (prev.next.data == key) {
                return prev;
                //如果不是继续往后找
            } else {
                prev = prev.next;
            }
        }
        //由于Node 为引用类型 那么返回null 代表一个节点的属性
        // data next
        return null;
    }

    public void remove(int key) {
        if (this.head == null) {
            return;
        }
        //如果要删的key就是头节点中的数据
        if (this.head.data == key) {
            this.head = this.head.next;
            return;
        }
        //当要删除的不是头节点时 要删除的在后面
        //1.先找到要删除元素的前驱(前一个)
        //创建一个方法
        Node prev = searchprev(key);
if(prev==null){
    System.out.println("根本没有这个节点");
    return ;
}
//通过Node引用del 确定del的位置 
// del指向我们要删除的节点
Node del=prev.next;
//再一次进行 
prev.next=del.next;
    }
}

八 删除所有值为key的节点

假如把所有值为12的删除

搞出来prev与cur 

这个方法思想是综合与提升

 这个方法只遍历一次链表就可以删除所有关键字为key的元素

注意 先删头后面的节点到最后再返回过来看头

prev 在cur 前一个进行定义通过cur的移动变化 prev进行变化

public void removeAllKey(int key){

        //先判断 从 头 后一个 是不是我们要删除的key
         //最后判断头是不是要删的
        
        //Node定义引用一个prev指向头 Node本身就是引用类型
        Node prev=this.head;
        //定义一个cur 指向头的下一个
        Node cur=this.head.next;
        while(cur!=null){
            //当cur指向的节点中存的数据等于key时 
            // 我们删除
            if(cur.data==key){
                prev.next=cur.next;
                cur=cur.next;
                
                //注意删就删了 不可以把prev往后
                // 如果往后就会可能有忽略产生 自己画图想想看
                
                
            } else {
                //如果不是 继续往后找
                
                prev=cur;//把prev指向cur所指向的位置
                cur=cur.next;
                
            }
            //最后看头 是否是key
            if(this.head.data==key){
                this.head=this.head.next;
            }
        }
    }
}

九 释放链表

 相当于诺米骨牌 推倒第一个就可以了

public void clear(){
    this.head=null;
}

总代码如下

//Node 相当于一个节点 这个节点包括next和data
//Node 表示一个类型

public class Eriksen {

class Node {
    public int data;
    //next中存放一个地址 定义为Node类型
    public Node next;


    public Node(int data) {
        this.data = data;
        this.next = null;
    }

    public Node head;

    public void addFirst(int data) {
        Node node = new Node(data);
        if (this.head == null) {
            this.head = node;
            return;
        }
        node.next = this.head;
        this.head = node;
    }

    public void display(int data) {
        Node cur = this.head;
        while (cur != null) {
            System.out.println(cur.data);
            cur = cur.next;
        }
    }

    public void addLast(int data) {
        Node node = new Node(data);
        if (this.head == null) {
            this.head = node;
            return;
        }
        Node cur = this.head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = node;
    }

    public boolean contains(int key) {
        Node cur = this.head;
        while (cur != null) {
            if (cur.data == key) {
                return true;
            }
        }
        return false;
    }

    public int size() {
        Node cur = this.head;
        int count = 0;
        while (cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

    private Node searchIndex(int index) {
        //先堆index检查是否合法
        if (index < 0 || index > this.size()) {
            throw new RuntimeException("index位置不合法");
        }
        Node cur = this.head;
        while (index - 1 != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    //  表示要在下标为index处 插入数据为data的节点
    public void addIndex(int index, int data) {
        Node node = new Node(data);
        //当index为0时 相当于头插
        if (index == 0) {
            addFirst(data);
            return;
        }
        //当index等于长度时 等价于尾插
        if (index == this.size()) {
            addLast(data);
            return;
        }

        //先找到index位前面一个节点的地址
        Node cur = searchIndex(index);

        //找到之后进行插入

        node.next = cur.next;
        cur.next = node;
    }

    private Node searchprev(int key) {
        Node prev = this.head;
        while (prev.next != null) {
            //如果下一个数据等于key时 返回prev
            if (prev.next.data == key) {
                return prev;
                //如果不是继续往后找
            } else {
                prev = prev.next;
            }
        }
        //由于Node 为引用类型 那么返回null 代表一个节点的属性
        // data next
        return null;
    }

    public void remove(int key) {
        if (this.head == null) {
            return;
        }
        //如果要删的key就是头节点中的数据
        if (this.head.data == key) {
            this.head = this.head.next;
            return;
        }
        //当要删除的不是头节点时 要删除的在后面
        //1.先找到要删除元素的前驱(前一个)
        //创建一个方法
        Node prev = searchprev(key);
          if(prev==null){
       System.out.println("根本没有这个节点");
      return ;
   }
//通过Node引用del 确定del的位置
// del指向我们要删除的节点
Node del=prev.next;
//再一次进行
prev.next=del.next;
    }
    public void removeAllKey(int key){

        //先判断 从 头 后一个 是不是我们要删除的key
         //最后判断头是不是要删的

        //Node定义引用一个prev指向头 Node本身就是引用类型
        Node prev=this.head;
        //定义一个cur 指向头的下一个
        Node cur=this.head.next;
        while(cur!=null){
            //当cur指向的节点中存的数据等于key时
            // 我们删除
            if(cur.data==key){
                prev.next=cur.next;
                cur=cur.next;

                //注意删就删了 不可以把prev往后
                // 如果往后就会可能有忽略产生 自己画图想想看


            } else {
                //如果不是 继续往后找

                prev=cur;//把prev指向cur所指向的位置
                cur=cur.next;

            }
            //最后看头 是否是key
            if(this.head.data==key){
                this.head=this.head.next;
            }
        }
    }
}

    public static void main(String[] args) {
        Eriksen nod=new Eriksen();
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值