自定义单向链表实现(详细)

一、链表介绍

这里写图片描述

链表结构如上所示(图画的不好请原谅),链表中存放着一个个Node,而每个Node又分为两部分,前一部分存放着Data,而后一部分存放着下一个节点的引用,这样一层层引用下去的一种结构。所以链表的结构决定了它的存储并不需要连续的存储空间。这样的链表访问头数据比较快,另外删除也比较快,但是查找靠后的数据稍微慢了点。

二、自定义链表的实现

1. 节点类的实现

因为链表是由节点组成的,所以先实现节点

public class Node {
    Node next=null;
    public int data;

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

节点类很简单,两部分,第一部分存放着数据(这里数据用的int,也可以自定义其他类型),第二部分用来存放下一个节点的引用,我们暂时定义为null。

2. 链表类的实现

public class MyLinkedList {
    public Node head=null;
    }

现在的链表类很简单,只有一个head,因为我们链表一般对外只提供头结点,所以只需要一个head就足够。下面介绍链表方法实现。

2.1 得到链表的长度

 /**
     * @return 返回此链表长度
     */
    public int length(){
        int length=0;
        Node temp=head;
        while(temp!=null){
            temp=temp.next;
            length++;
        }
        return length;
    }

上面代码返回链表长度

2.2 为链表增加节点

2.2.1 尾部添加新节点

这里写图片描述
如图,在尾部添加节点,只需要把目前最后一个链表的next指向新节点就OK啦。

/**
     * 向链表结尾中添加数据
     * @param d 插入的数据
     */
    public void addNode(int d){
        Node newNode=new Node(d);
        if(head==null){//如果此链表中没有数据。
            head=newNode;
            return;
        }
        Node temp=head;
        while(temp.next!=null){
            temp=temp.next;
        }
        temp.next=newNode;
    }

上面方法实现了向链表中增加节点,如果此链表是一个空链表,这直接给链表中的head赋值,即完成了添加新节点任务,否则我我这里定义了向链表尾部添加新节点,其实可以定义向头结点添加新节点,那样效率更高。下面代码就是向头结点添加数据实现:

2.2.2 在头部添加新节点

这里写图片描述
如上图所示,在头部添加新节点,只需要把新节点的next指向原来的head节点,然后再把新节点赋值给head就搞定啦。

/**
     * 向头部添加节点
     * @param d
     */
    public void addNodeAtFirst(int d){
        Node newNode=new Node(d);
       newNode.next=head;
        head=newNode;
    }

2.2.3 其实还可以向任意位置添加节点:

这里写图片描述
在index位置添加新节点,需要先把index-1位置的next指向新节点,再把新节点的next指向原来在index位置的节点

 /**
     * 向链表中添加数据
     * @param data 插入的数据
     * @param index 插入数据的位置  如果是向头部添加数据,这直接调用addNodeAtFirst,否则需要判断0<index<length()
     * @return boolean 成功返回true 否则返回false
     */
    public boolean addNode(int data,int index){
        if(index==0){//向头部添加数据
            addNodeAtFirst(data);
            return true;
        }
        int length=length();
        if(index<1||index>=length) return false;//index不符合规则
        Node newNode=new Node(data);
        int i=1;
        Node temp=head;
        while(temp.next!=null){
            if(i==index){
                newNode.next=temp.next;
                temp.next=newNode;
                return true;
            }else {
                i++;
                temp=temp.next;
            }
        }
        return false;
    }

2.3 删除链表中的数据

2.3.1 删除头结点中数据

这里写图片描述
如上图所示,删除头结点只需要把链表中的head节点转移到head节点的下一个节点。
如:head=head.next;

2.3.2 删除末节点

这里写图片描述
删除末尾节点只需要把为节点的上一个节点的next赋值为null就可以了。

2.3.3删除其他节点

这里写图片描述
删除其他节点如图,把要删除的index节点的上一个节点的next直接赋值给index节点下一个节点。言语表达有点晦涩难懂,但是大家看图片还是比较容易弄明白的哈。
下面上代码,删除尾节点和头结点的代码就不单独列出了。下面其实都包括了

/**
     * 删除指定位置的链表元素   0<=index<length
     * @param index 要被删除的链表
     * @return 删除成功返回true 否则返回false
     */
    public boolean deleteNode(int index){
        int length=length();
        if(index<0||index>=length) return false;//未知输入错误
        if(index==0){//删除第一个元素
            head=head.next;
            return true;
        }
        Node preNode=head;
        Node curNode=head.next;
        int i=1;
        while (curNode!=null){
            if(index==i){
                preNode.next=curNode.next;
                return true;
            }else{
                i++;
            }
            preNode=curNode;
            curNode=curNode.next;
        }
        return false;
    }

这里不用担心存在内存泄漏,因为当你把要删除节点的对外引用去掉了以后,这个节点会自动呗gc删除,所以没有内存泄漏哦。

2.4 对链表排序

/**
     * 对链表进行排序(按照数据data从小到大排序,这里的data是int类型)  运用了插入排序算法
     * @return 返回排序后链表的头点
     */
    public Node orderList(){
        Node nextNode=null;
        Node curNode=head;
        while(curNode!=null){
            nextNode=curNode.next;
            while(nextNode!=null){
                if(nextNode.data<curNode.data){//data数据类型交换
                    curNode.data=curNode.data-nextNode.data;
                    nextNode.data=curNode.data+nextNode.data;
                    curNode.data=nextNode.data-curNode.data;
                }
                nextNode=nextNode.next;
            }
            curNode=curNode.next;
        }
        return head;
    }

上面代码实现了对链表的排序方法,用的是插入排序算法:
每一个元素分别和第一个元素比较大小,小的和第一个元素交换位置.第一次循环后,最小的就在最前面了。
然后从第二各元素开始。第二个元素和第二个后面元素比较大小,小的放在第二个元素后面。
。。。

3. 测试结果

自定义简单链表测试了一下,算是达到预期吧。如果有错误请指正。谢谢

package com.example.yang.mylinkedlistdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView text;
    private MyLinkedList list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text=(TextView)findViewById(R.id.text);
        list=new MyLinkedList();
        list.addNode(4);
        list.addNode(2);
        list.addNode(1);
        list.addNodeAtFirst(999);
        list.addNode(998,1);
        list.addNode(100);
        list.addNode(55);
        text.setText("");
        pritlnList();
        list.deleteNode(2);
        pritlnList();
        list.orderList();
        pritlnList();
    }

    private void pritlnList() {
        Node tmp=list.head;
        while (tmp!=null){
            text.append(tmp.data+",");
            tmp=tmp.next;
        }
        text.append("\n");
    }
}

用的Android Studio测试的
下面是结果图片:
这里写图片描述

三、链表中的其他方法实现

1. 删除链表中的重复数据

1.1 时间优先

/**
     * 时间复杂度低
     * 删除重复
     */
    public void deleteDuplecateTime(){
        Hashtable<Integer,Integer> table=new Hashtable<>();
        Node temp=head;
        Node pre=null;
        while(temp!=null){
            if(table.containsKey(temp.data)){
                pre.next=temp.next;
            }else {
                table.put(temp.data,1);
                pre=temp;
            }
            temp=temp.next;
        }
    }

实现方法是遍历值存储在一个Hashtable中,遍历中查询Hahstable中是否存在此元素,存在就删除。利用了Hashtable中的Key不能重复这一特点。

1.2 空间优先

/**
     * 空间复杂度低
     * 删除重复
     */
    public void deleteDuplecateSpace() {
        Node curNode = head;
        Node nextNode = null;
        while (curNode != null) {
            nextNode = curNode;
            while (nextNode.next != null) {
                if (curNode.data == nextNode.next.data) {
                    nextNode.next = nextNode.next.next;
                }
                nextNode = nextNode.next;
            }
            curNode=curNode.next;
        }
    }

思路类似与插入排序算法,两层循环,第一层正常遍历,第二层从第一层的开始位置遍历,然后每个都和第一层的比较,如果数据相同,就删除。
优点为空间占用小,确定为时间占用会比第一种方法大一些。

2. 找出单链表中倒数第K个元素

先说一下算法思路,为了只遍历一次就能得到倒数第K个元素:
设置两个指针,第一个指针比第二个指针快k-1步,然后两个指针一起向后遍历
当第一个指针到尾节点(尾节点的next=null)时,第二个指针的位置就是倒数第k个元素的位置

 /**
     * 找到倒数第k个元素
     * @param k     0<k<=length
     * @return 找到的节点
     */
    public Node findElemAtLast(int k){
        if(k<1) return null;
        Node p1=head;
        Node p2=head;
        if(p1==null) return null;
        for(int i=0;i<k-1;i++){
            p1=p1.next;
            if(p1==null) return null;
        }
        while(p1.next!=null){
            p1=p1.next;
            p2=p2.next;
        }
        return p2;
    }

3. 链表的反转

猜你喜欢

转载自blog.csdn.net/qq_34557284/article/details/70196107
今日推荐