带头与不带头节点单链表

一、链表与顺序表区别
二、链表的结构
1、带头节点
2、不带头节点
三、实现一个带头节点的链表
方法:
1、增加(头插、尾插)
2、删除某个节点
3、输出链表
4、得到链表指定位置节点
5、逆置链表
6、得到链表长度
四、不带头结点链表
方法:
1、增加(头插、尾插)
2、删除某个节点
3、输出链表
4、得到链表指定位置节点
5、逆置链表
6、得到链表长度
7、查找链表倒数第k个节点
8、合并两个有序链表
9、逆序输出链表
10、不允许遍历链表,在 pos 前插入一个节点

一、链表与顺序表区别
顺序表:
底层实现是数组,是一段连续的内存空间;
遍历和查找元素比较容易,插入删除比较复杂;
数组的大小确定后,要更改需要花费较大的代价。

链表:
对于每一个链表来说,都有一个数据域和一个引用域,这个引用域是链表下一个节点的引用;
对于链表来说,插入删除元素容易,遍历链表相对顺序表来说困难;
同时,链表的物理位置不确定,可能是一段连续的内存空间、也可能不是一块连续的内存空间,由每个节点的引用域确定。

二、链表的结构
这里有一个技巧,也就是设计链表时用到的一个名叫头节点的东西–>head。
在带头节点的链表中,头节点的意义是:在单链表的第一个结点之前附设的一个结点
不带头节点的链表中,头节点仅仅只是标记着链表中第一个元素的位置。
1、带头节点
在这里插入图片描述
2、不带头节点
在这里插入图片描述
三、实现一个带头节点的链表

class linkedListWithHead<T> {
    // head 是指向链表中第一个节点的引用
    protected Node<T> head;

    class Node<E> {

        protected E element;//数据域
        protected Node<E> next;//引用域

        public Node(E element) {
            this.element = element;
        }

    }
	//为 head 开辟空间
    public linkedListWithHead() {
        head = new Node<>((T) new Object());
    }

    //头插
    public void addHead(T data) {
        //创建一个新节点
        Node<T> newNode = new Node<>(data);
        //绑定新节点
        newNode.next = head.next;
        head.next = newNode;
    }

    //尾插
    public void addTail(T data) {
        //创建一个新节点
        Node<T> newNode = new Node<>(data);
        Node<T> temp = head;
        //先遍历,找到最后一个节点
        while (temp.next != null) {
            temp = temp.next;
        }
        //绑定新节点
        temp.next = newNode;
    }

    //输出链表  重写 toString()方法
    public String toString() {
        StringBuilder sbr = new StringBuilder();
        Node<T> temp = head;
        while (temp.next != null) {
            sbr.append(temp.next.element + " ");
            temp = temp.next;
        }
        return sbr.toString();
    }

    //删除某个值
    public boolean delete(T data) {
        if (head.next == null) return false;
        //判断头节点是不是要被删除的元素
        if (head.element == data) {
            head = head.next;
            return true;
        }
        Node<T> temp = head;
        //在普通节点里面寻找要被删除的元素
        while (temp.next != null) {
            if (temp.next.element == data) {
                temp.next = temp.next.next;
                return true;
            }
            temp = temp.next;
        }
        return false;
    }

    //逆置链表  以头插的方式完成
    /*
    用 temp 指向链表中第一个数的引用,从第一个数开始挪
    temp = head.next;   head.next = null;
    将 temp 的后面的数即链表中第二个数先保存 tempNext = temp.next
    此时链表分为头节点、链表中第一个数、第二个数及其后的数
    此时 temp 已经孤立,可以对其操作
    把 temp 以头插的方式插入到链表中
    以此类推...
     */
    public void reverseMineLikeInsertHead() {
        //空链表则返回
        if (head.next == null) return;
        Node<T> temp = head.next;
        head.next = null;
        while (temp != null) {
            Node<T> tempNext = temp.next;
            temp.next = head.next;
            head.next = temp;
            temp = tempNext;
        }
    }

    //得到指定位置的节点元素  下标:0 1 2 3 4
    public T getIndexValue(int index) {
        //链表为空则返回
        if (head.next == null || index < 0) return null;
        Node<T> temp = head;
        while (temp.next != null && index-- > 0) {
            temp = temp.next;
        }
        if (index <= 0) {
            return temp.next.element;
        }
        //下标:0, 1, 2, 3
        return null;
        //return temp.element, 下标:1, 2, 3, 4
    }

    //得到链表长度
    public int getLength() {
        //链表为空则返回
        if (head.next == null) return -1;
        int size = 0;
        Node<T> temp = head;
        while (temp.next != null) {
            size++;
            temp = temp.next;
        }
        return size;
    }
}

四、不带头结点链表

class LinkedListWithoutHead<T extends Comparable<T>> {

    class Node<E> {

        public E element;
        public Node<E> next;

        public Node(E data) {
            this.element = data;
        }
    }

    //head 标记着链表的第一个元素
    public Node<T> head;

    public LinkedListWithoutHead() {
        head = null;
    }

    //头插
    public void addHead(T data) {
        //第一次创建的时候,newNode 是第一个节点, head是链表中第一个节点的标记
        //第二次
        Node<T> newNode = new Node<>(data);
        //当前链表中已有节点,让 head 只作为链表中第一个元素的标记
        if (head != null) {
            //此时 newNode 是第二个节点,它的 next 域是链表中第一个节点,这个节点用 head 标记
            newNode.next = head;//把 head 的引用赋给第二个节点的 next 域
            //以此类推...
        }
        //当前链表中没有节点,让 head 做标记
        //已有,更新 head
        head = newNode;
    }

    //尾插
    public void addTail(T data) {
        Node<T> newNode = new Node<>(data);
        //仍然判断链表是否为空
        if (head != null) {
            //head 是链表中第一个元素的标记,不能随便搞
            Node<T> temp = head;
            //遍历链表,找到链表中最后一个元素
            while (temp.next != null) {
                temp = temp.next;
            }
            temp.next = newNode;
            //有 return 语句不用 else 语句
//            return;
        } else {//链表为空,让 head 做链表第一个元素的标记
            head = newNode;
        }
    }

    //删除
    public boolean delete(T value) {
        if (head == null) return false;
        if (value == head.element) {
            head = head.next;
            return true;
        }
        Node<T> temp = head;
        while (temp.next != null) {
            if (value == temp.next.element) {
                temp.next = temp.next.next;
            }
            temp = temp.next;
        }
        return false;
    }

    //在指定位置添加一个节点
    public boolean addInGivenPosition(T data, int pos) {
        if (pos < 0 || pos > getLength() + 1 || head == null) return false;
        Node<T> newNode = new Node<>(data);
        if (pos == 0) {
            newNode.next = head;
            head = newNode;
            return true;
        }
        Node<T> temp = head;
        //大于 1 可以添加在指定下标
        while (temp.next != null && pos-- > 1) {
            temp = temp.next;
        }
        Node<T> tempNode = temp.next;
        temp.next = newNode;
        newNode.next = tempNode;
        return true;

    }

    //以头插的方式逆置链表
    public void reverseLikeInsertHead() {
        //链表为空或只有一个节点,不用逆置
        if (head == null || head.next == null) return;
        if (head.next != null) {
            Node<T> temp = head.next;
            head.next = null;
            while (temp != null) {
                Node<T> tempNext = temp.next;
                temp.next = head;
                head = temp;
                temp = tempNext;
            }
        }
    }

    //打印链表   toString
    public String toString() {
        StringBuilder sbr = new StringBuilder();
        //此时head 指向链表中第一个元素
        Node<T> temp = head;
        while (temp != null) {
            sbr.append(temp.element + " ");
            temp = temp.next;
        }
        return sbr.toString();
    }

    //打印链表  循环
    public void showLink() {
        Node<T> temp = head;
        while (temp != null) {
            System.out.print(temp.element + " ");
            temp = temp.next;
        }
        System.out.println();
    }

    //链表长度
    public int getLength() {
        int size = 0;
        Node<T> temp = head;
        while (temp != null) {
            size++;
            temp = temp.next;
        }
        return size;
    }

    //返回指定节点
    public T getIndexValue(int index) {
        if (head == null || index < 0) return null;
        Node<T> temp = head;
        while (temp != null && index-- > 0) {
            temp = temp.next;
        }
        if (index <= 0) {
            return temp.element;
        }
        return null;
    }

    //返回链表的头
    public Node<T> getHead() {
        return head;
    }

    //设置链表的头
    public void setHead(Node<T> head) {
        this.head = head;
    }

    //合并两个有序列表
    public Node<T> mergeMyLink(Node<T> head1, Node<T> head2) {
        if (head1 == null) return head2;
        if (head2 == null) return head1;
        Node<T> curHead;
        if (head1.element.compareTo(head2.element) < 0) {
            curHead = head1;
            head1 = head1.next;
        } else {
            curHead = head2;
            head2 = head2.next;
        }
        Node<T> temp = curHead;
        while (head1 != null && head2 != null) {
            if (head1.element.compareTo(head2.element) <= 0) {
                temp.next = head1;
                head1 = head1.next;
                temp = temp.next;
            } else {
                temp.next = head2;
                head2 = head2.next;
                temp = temp.next;
            }
        }
        if (head1 == null) {
            temp.next = head2;
        } else if (head2 == null) {
            temp.next = head1;
        }
        head = curHead;
        return head;
    }

    //逆序输出单链表
    public  <T> void reversePrintList(Node<T> head){
        //递归终止条件
        if(head == null){
            return;//处理办法
        }
        //提取重复逻辑,缩小问题规模
        reversePrintList(head.next);
        System.out.print(head.element + " ");
    }

    //查找单链表中倒数第K个元素
    public T findReverseValue(LinkedListWithoutHead<T> list,int k){
        if (k <= 0 || k > list.getLength()) return null;
        //倒数第 k 个不就是 list.getLength() - k 
        int index = list.getLength() - k;
        Node<T> temp = head;
        while (temp != null && index-- >0){
            temp = temp.next;
        }
        if (index <= 0){
            return temp.element;
        }
        return null;
    }

	//查找链表倒数第k个元素  高效算法
	public Node<T> findElem(Node<T> head, int k) {
        if(k < 1 || head == null) {
            return null;
        }
        Node<T> front = head;
        Node<T> behind = head;
        /*
		搞一个快引用,一个慢引用
		让快引用先走它个 k-1 米
		当它还可以继续给后走
		快、慢一起做运动
		当快引用走到最后一个时
		慢引用恰好走到倒数第 k 个
		努力运动、好好 keep 哦
 		*/
        for(int i=0; i<k-1; i++){
            if(front.next != null){
                front = front.next;
            }else{
                return null;
            }
        }
        while(front.next != null){
            front = front.next;
            behind = behind.next;
        }
        return behind;
    }

    //不允许遍历链表,在 pos 前插入 data
    /*
    先把新节点放到 pos 后,
    把 pos.element 赋值给新的节点
    再把 data 赋值给 pos.element
    偷天换日,假装插到了 pos 前
     */
       public void NoCycleInsertBeforePos(LinkedListWithoutHead<T> list, Node<T> pos, T data){
        if (list.head == null || pos == null) return;
        Node<T> newNode = new Node<>(data);
        newNode.next = pos.next;
        pos.next = newNode;
        newNode.element = pos.element;
        pos.element = data;
    }


}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值