数据结构与算法——单链表

文章目录
1.单链表的性质及其优、缺点
2.不带头节点的链表的插入删除操作
3.带头节点的单链表
4.不带头节点的链表的基础操作:

(1)插入(头插法与尾插法)
(2)删除
(3)链表的逆置
(4)获取链表倒数第K个节点的值
(5)判断链表是否有环
(6)合并两个有序链表

1.单链表的性质及其优、缺点

性质:
1.链表是以节点的方式来存储的
2.每个节点包含data域和next域
3.链表的各个节点不一定是连续存储的
4.链表分为带头节点的链表和不带头节点的链表
优点:
1.单链表的使用使得内存的使用效率提高,在存储的数据量较大时,不需要大片连续的内存空间
2.数据的插入、删除操作的时间复杂度时O(1),常量时间,速度非常快
缺点:
1.链表的搜索操作需要遍历链表,时间为线性时间O(n),数据量大的时候,搜索效率太低

2.不带头节点的链表

不带头接节点的单链表的基础操作如下:
(1)插入(头插法与尾插法)
(2)删除

不带头节点的单链表的代码实现:

package unheadlink;

public class LinkedTest {
    /**
     * 单链表的节点类型
     */
    private static class Entry<T>{
        T data;//单链表的数据域
        Entry<T> next;//下一个节点的地址

        public Entry(T data, Entry<T> next) {
            this.data = data;
            this.next = next;
        }
    }
    /**
     * 不带头节点的单链表的实现
     */
    static class Link<T>{
        public Entry<T> head;//指向单链表的第一个节点

        public Link() {
            this.head=null;
        }
        /**
         * 单链表的头插法
         */
        public void insertHead(T val){
            Entry<T>  newNode = new Entry<>(val,this.head);
            this.head = newNode;

        }
        /**
         * 单链表的尾插法
         */
        public void insertTail(T val){
            Entry<T> newNode = new Entry<T>(val,null);
            //头节点为空时,直接插入
            if (this.head==null){
                head = newNode;
                return;
            }
            //头节点不为空
            Entry<T> cur = head; //指针指向头节点
            while (cur.next != null){ //用指针遍历链表,直到找到链表的最后一个节点
                cur=cur.next;
            }
            cur.next=newNode;//找到了最后一个节点,将新节点插入
        }
        
    }

    public static void main(String[] args) {
        Link<Integer> link = new Link<>();
        link.insertHead(10);
        link.insertHead(20);
        link.insertHead(15);
        link.show();
        System.out.println();

        link.insertTail(20);
        link.insertTail(26);
        link.insertTail(31);
        link.show();
        System.out.println();

        link.remove(20);
        link.remove(26);
        link.remove(31);
        link.show();
        System.out.println();

    }
}


3.带头节点的单链表

不带头节点的链表的基础操作:
(1)插入(头插法与尾插法)
(2)删除

(3)链表的逆置
(4)获取链表倒数第K个节点的值
(5)判断链表是否有环
(6)合并两个有序链表

/*
    不带头节点的单链表的实现
 */
  /**
     * 单链表的节点类型
     */
     static class Entry<T>{
        T data;//单链表的数据域
        Entry<T> next;//下一个节点的地址

        public Entry(T data, Entry<T> next) {
            this.data = data;
            this.next = next;
        }
    }
    /**
     * 单链表的头节点类型
     */
     static class HeadEntry<T> extends Entry<T>{
        int cnt;//用来记录节点个数

        public HeadEntry(int cnt,T data, Entry<T> next) {
            super(data, next);
            this.cnt=cnt;
        }
    }

    /**
     * 带头节点的单链表实现
     */
    class Link<T extends Comparable<T>>{
        //指向链表的第一个节点
        HeadEntry<T> head;

        public Link() {
            this.head = new HeadEntry<T>(0,null,null);
        }
        /**
         *  单链表的头插法
         */
        public void insertHead(T val){
            Entry<T> node = new Entry<>(val,this.head.next);
            this.head.next = node;
            this.head.cnt +=1;//更新头节点中,节点的数量
        }

        /**
         * 单链表的尾插法
         * @param val
         */
        public void insertTail(T val){
            Entry<T> node = head;
            while (node.next!=null){
                node = node.next;
            }
            node.next = new Entry<>(val,null);
            this.head.cnt +=1; //跟新头节点中节点个数
        }
        /**
         * 单链表中删除值为val的值
         */
        public void remove(T val){
            Entry<T> pre = head;
            Entry<T> cur = head.next;
            while (cur!=null){
                if (cur.data==val){
                    //刚好初始cur指针指向的是要删除的val,删除val
                    pre.next=cur.next;
                    this.head.cnt +=1;//更新头节点中链表节点的个数
                    //break;  只删除第一个值尾val的节点
                    cur = pre.next; // 删除链表中所有值为val的节点
                }
               else {
                    pre = cur;
                    cur =cur.next;
                }
            }
        }
        /**
         * 打印单链表所有元素的值
         */
        public void show(){
            Entry<T> temp = this.head.next;
            while (temp!=null){
                System.out.println(temp.data+" ");
                temp = temp.next;
            }
            System.out.println();
        }

        /**
         * 获取链表节点 的数目
         * @return
         */
        public int getNodeCount() {
            return this.head.cnt;
        }
        /**
         * 单链表的逆置
         * 逆置思想概述:从链表的第二个节点开始,采用头插法
         */
        public void reverse(){
            if(this.head.next==null){
                return;
            }
            Entry<T> cur = this.head.next.next;//首先用cur指针指向第一个需要进行头插的节点
            this.head.next.next = null; //将其置为null
            Entry<T> post = null;

            while (cur!=null){
                post=cur.next; //用一个指针指向下一个待头插的节点
                cur.next = head.next;//1.head.next = cur 2.cur = post=cur.next  ==>cur.next = head.next
                head.next = cur;
                cur = post;
            }
        }
        /**
         * 获取倒数第K个单链表节点的值
         * cur1    cur2
         * @param k
         * @return
         */
        public T getLastK(int k){
            Entry<T> cur1 = this.head.next;
            Entry<T> cur2 = this.head;

            //cur1指向正数第一个节点 cur2指向正数第K个节点
            for (int i=0;i<k;i++){
                cur2 = cur2.next;
                if (cur2==null){
                    return null;
                }
            }
            // 当cur2到达末尾节点时,cur1指向的就是倒数第k个节点
            while (cur2.next!=null){
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1.data;
        }

    }
    /**
         * 判断链表是否有环,如果有,返回入环节点的值,没有环,返回null
         * 入环的节点特征:
         * @return
         */
        public T getLinkCircleVal(){
            //使用快慢指针来解决此类问题
            Entry<T> slow = this.head.next;
            Entry<T> fast = this.head.next;

            while (fast!=null&&fast.next!=null){
                slow = slow.next;
                fast = fast.next.next;
                if (slow==fast){
                    break;
                }
            }
            if (fast==null){
                return null;
            } else {
                // fast从第一个节点开始走,slow从快慢指针相交的地方开始走,
                // 它们相遇的时候,就是环的入口节点
                fast = this.head.next;
                while (fast!=slow){
                    fast=fast.next;
                    slow=slow.next;
                }
                return slow.data;
            }
        }
                /**
         * 合并两个有序的单链表
         * @param link
         */
        public void merge(Link<T> link){
            Entry<T> p = this.head;
            Entry<T> p1 = this.head.next;
            Entry<T> p2 = this.head.next;
            // 比较p1和p2节点的值,把值小的节点挂在p的后面
            while (p1!=null && p2!=null){
                if (p1.data.compareTo(p2.data)>0){
                    p.next = p2;
                    p2=p2.next;
                }else {
                    p.next = p1;
                    p1 = p1.next;
                }
                p = p.next;
            }
            if(p1 != null){ // 链表1还有剩余节点
                p.next = p1;
            }

            if(p2 != null){ // 链表2还有剩余节点
                p.next = p2;
            }
        }


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值