《算法通关村第一关——链表青铜挑战笔记》Java

本文详细介绍了链表的基本概念,包括节点和头节点,以及JVM如何构造链表。内容涵盖链表的增加和删除操作,以及双向链表的构建、遍历、插入和删除方法,用Java实现实例详解。
摘要由CSDN通过智能技术生成

链表的相关概念

节点和头节点

链表中,每个节点由值和指向下一个节点的地址组成,也称为结点。

第一个节点称作头结点。知道了头结点就可以遍历整个链表。

虚拟节点

其实就是一个节点dummyNode,其next指针指向head。在算法中要获取head节点,或者从方法(函数)里返回的时候,则应使用dummyNode。

另外注意,dummmyNode的值不会被使用,可以初始化为0或-1,它的作用只是用来处理首部节点。

JVM是如何构造链表的

public class BasicLink {

    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4, 5, 6};
        Node head = initLinkedList(a);
        System.out.println(head);
    }

    private static Node initLinkedList(int[] array) {
        Node head = null, cur = null;
        for (int i = 0; i < array.length; i++) {
            Node newNode = new Node(array[i]);
            newNode.next = null;
            if (i == 0) {
                head = newNode;
                cur = newNode;
            } else {
                cur.next = newNode;
                cur = newNode;
            }
        }
        return head;
    }

    static class Node {
        public int val;
        public Node next;

        Node(int x) {
            val = x;
            next = null;
        }
    }
}

链表的增加和删除

/**
 * 构造链表,使用静态内部类定表示结点,实现增加和删除元素的功能
 *
 */
public class BasicLinkList {
    static class Node {
        final int data;
        Node next;
        public Node(int data) {
            this.data = data;
        }
    }
     /**
     * 获取链表长度
     *
     * @param head 链表头节点
     * @return 链表长度
     */
    public static int getLength(Node head) {
        int length = 0;
        Node node = head;
        while (node != null) {
            length++;
            node = node.next;
        }
        return length;
    }

    /**
     * 链表插入
     *
     * @param head       链表头节点
     * @param nodeInsert 待插入节点
     * @param position   待插入位置,取值从2开始
     * @return 插入后得到的链表头节点
     */
    public static Node insertNode(Node head, Node nodeInsert, int position) {
        // 需要判空,否则后面可能会有空指针异常
        if (head == null) {
            return nodeInsert;
        }
        //越界判断
        int size = getLength(head);
        if (position > size + 1 || position < 1) {
            System.out.println("位置参数越界");
            return head;
        }

        //在链表开头插入
        if (position == 1) {
            nodeInsert.next = head;
//            return nodeInsert;
            //上面return还可以这么写:
            head = nodeInsert;
            return head;
        }

        Node pNode = head;
        int count = 1;
        while (count < position - 1) {
            pNode = pNode.next;
            count++;
        }
        nodeInsert.next = pNode.next;
        pNode.next = nodeInsert;

        return head;
    }

    /**
     * 删除节点
     *
     * @param head     链表头节点
     * @param position 删除节点位置,取值从1开始
     * @return 删除后的链表头节点
     */
    public static Node deleteNode(Node head, int position) {
        if (head == null) {
            return null;
        }
        int size = getLength(head);
        //思考一下,这里为什么是size,而不是size+1
        if (position > size || position <1) {
            System.out.println("输入的参数有误");
            return head;
        }
        if (position == 1) {
            //curNode就是链表的新head
            return head.next;
        } else {
            Node cur = head;
            int count = 1;
            while (count < position - 1) {
                cur = cur.next;
                count++;
            }
            Node curNode = cur.next;
            cur.next = curNode.next;
        }
        return head;
    }

    /**
     * 输出链表
     *
     * @param head 头节点
     */
    public static String toString(Node head) {
        Node current = head;
        StringBuilder sb = new StringBuilder();
        while (current != null) {
            sb.append(current.data).append("\t");
            current = current.next;
        }
        return sb.toString();
    }

}

双向链表是如何构造的

它在单项链表的基础上新增了对向前指向的结点。

双向链表的结点结构:

class ListNode {
	int data;
	Node prev; // 前一个节点的引用
	Node next; // 后一个节点的引用

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

双向链表的遍历方式(循环和递归)或者从前往后和从后往前

双向循环从前往后循环

Node current = head;
while (current!= null){
	System.out.println(current.data);//操作数据
	current = current.next
}

双向链表从后往前递归

public void traverse(Node node){
	if(node!= null){
		System.out.println(node.data);//操作数据
		traverse(node.prev);
	}
	return;//递归终止条件	
}
traverse(tail);//tail为尾部节点

双向链表的插入和删除

    //头插法
    public void addFirst(int data){
        //创建新节点
        ListNode node = new ListNode(data);
        //处理是空链表时的情况
        if(this.head == null){
            this.head = node;
        }else{
            //正常情况,next和prev都要进行处理
            node.next = this.head;
            this.head.prev = node;
            this.head = node;
        }
    }
//尾插法 如法炮制
	public void addLast(int data){
		ListNode node = new ListNode(data);
		if(this.next == null){
			this.next = node;
		}else{
			node.prev = this.last;
			this.last.next = node;
			this.last = node;
		}
	}
//中间插入
/*
3.先处理在 index == 0 的情况下插入数据,直接使用头插法(addFirst)
4.再处理在 index == size()的情况下插入数据,使用尾插法(addLast)
5.最后处理正常情况(在双链表中只要找到了index指向的那个节点就可以直接插入新节点)
*/
public boolean addIndex(int index, int data){
	
	//1.先判是否为空链表
	if(this.head == null){
		return false;
	}
	//2.然后判断index(下标)的值是否合法,合法范围:0<= index<= size()(链表当前的节点个数)
	if(index < 0 || index > length()){
		return false;
	}
	//3.先处理在 index == 0 的情况下插入数据,直接使用头插法(addFirst)
	if(index == 0){
		addFirst(data);
		return true;
	}
	//4.再处理在 index == size()的情况下插入数据,使用尾插法(addLast)
	if(index == length()){
		addLast(data);
		return true;
	}
	//5.最后处理正常情况(在双链表中只要找到了index指向的那个节点就可以直接插入新节点)
	ListNode node = new ListNode(data);
	ListNode cur = this.head;
	while(index>0){
		cur = cur.next;
		index--;
	}
	node.next = cur;
	node.prev = cur.prev;
	cur.prev.next = node;
	cur.prev = node;
	return true;
}

删除结点

删除首结点

public void deleteFirst(){
	//若链表只有一个结点
	if(this.head.next == null){
		this.last = null;
	}else{
	this.head.next.prev = null;
	}
	this.head = this.head.next;
	return;
}

删除尾结点

public void deleteLast(){
	if(this.last.prev ==null){
		this.head = null;
	}else{
	this.last.prev.next = null;
	}
	this.last = this.last.prev;
	return;
}

删除中间结点

public boolean deleteIndex(int index){
	if(this.head.next == null){
		this.head=null;
		this.last=null;
		return false;
	}
	if(index < 1 || index > length()){
		return false;
	}
	if(index = 1){
		this.deleteFirst();
		return true;
	}else if(index = length()){
		this.deleteLast();
		return true;
	}else{
		DoubleListNode cur = this.head;
		while(index > 0){
			if(cur.value == key){
				cur.next = cur.prev.next;
				cur.prev = cur.next.prev;
				return true;
			}else{
				cur = cur.next;
				index--;
			}
		}
		return false;
	}
}

删除第一次val为key的结点 ——remove (int key)

public void remove(int key){
	//1.先判断是不是空链表
	if(this.head == null){
		return;
	}
	//2.处理head结点的value值为key的情况
	if(this.head.value == key){
		this.head = this.head.next;
		//注意,要清除前记录点
		this.head.prev = null;
		return;
	}
	//3.正常循环遍历,注意区分找到的结点是否为尾结点。
	ListNode cur = this.head;
	while(cur != null){
		if(cur.val == key){
			if(cur.next == null){
			//最后一个结点
				cur.last = cur.prev;
				cur.prev.next = null;
				cur.prev = null;
				return;
			}else{
				cur.prev.next = cur.next;
				cur.next.prev = cur.prev;
				return;
			}
		}else{
			cur = cur.next;
		}
	}
	return;
}

删除所有val值为key的结点——removeAll(int key)

    //删除所有值为key的节点
    public void removeAll(int key){
        //判断是否为空链表
        if(this.head == null){
            return;
        }
        //先处理正常情况
        ListNode cur = this.head.next;
        //循环遍历链表
        while(cur != null){
            //找到了
            if(cur.val == key){
                //先判断是不是尾节点
                if(cur.next == null){
                    //是尾节点,进行处理
                    cur.prev.next = null;
                    cur.prev = null;
                }else{
                    //不是尾结点,就这样处理
                    cur.prev.next = cur.next;
                    cur.next.prev = cur.prev;
                }
            }
            cur = cur.next;   //该节点是与不是都要往后走
        }
        //处理头节点的情况
        if(this.head.val == key){
            this.head = this.head.next;
            //判断头节点处理完毕后是不是成了一个空链表
            if(this.head != null){
                this.head.prev = null;
            }
        }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值