链表java实现(一)单链表的基本使用以及常见面试题

一、基本介绍

链表是有序列表,但它在内存中各节点却不一定连续,单链表在内存中的存储如下:
单链表示意图
  由上图可知:

  1. 链表以节点方式进行存储,是链式存储。
  2. 每个节点包含一个data域和next域,data域用来存储数据,next域则用来指向下一个节点。
  3. 链表的各个节点不一定连续存储。

一般的,链表可分为带头节点的和不带头节点的,具体使用要根据实际情况来分析。链表的逻辑结构如下:
带头节点单链表逻辑结构

二、链表的使用

1、首先需要定义一个链表结构

class Node {
/*
这里的data域值一系列想要存储的数据,一个节点可以存储多个数据,这里只写了一个data,可根据需要定义多个data
	例如: 
	public int data1;
	public int data2;
	public String data3;
    public Node next;
*/
	//这里为了快速实现就将变量定义为public
    public int data;
    public int data1;
    public Node next;
	
	//空参构造器
    public Node() {
    }
	//有参构造器
    public Node(int data,int data1) {
        this.data = data;
        this.data1 = data1;
    }
	//重写toString()方法
    @Override
    public String toString() {
        return "Node{" + "data=" + data + ", data1=" + data1 + '}';
    }
}

2、添加增删改查基本操作,这里我们再定义一个类来实现

class SingleLinkedList2 {
    //初始化一个头节点,不存放具体数据
    private Node head = new Node();

    //添加节点到单向链表,这里默认data唯一,不可重复添加
    public void add(Node node) {
        //辅助变量,用于遍历
        Node temp = head;
        boolean flag = false;
        //遍历链表,找到最后一个节点
        while (true) {
            //找到链表最后
            if (temp.next == null) {
                break;
            }
            if (temp.next.data == node.data) {
                flag = true;
                break;
            }
            //如果没有找到,就将temp后移
            temp = temp.next;
        }
        if (flag) {
            System.out.printf("节点发%d已经存在\n",node.data);
        } else {
            //当退出while循环时,temp就指向链表的最后
            temp.next = node;
        }
    }

    //按插入大小排序,这里默认data唯一,不可重复添加
   public void addByOrder(Node node) {

        Node temp = head;
        boolean flag = false;
        while (true) {
            if (temp.next == null) {//说明temp已经到链表最后
                break;
            }
            if (temp.next.data > node.data) {//位置找到,在temp后面添加
                break;
            } else if (temp.next.data == node.data) {
                flag = true;
                break;
            }
            temp = temp.next;//后移
        }
        //判断flag的值
        if (flag) {//已存在,添加失败
            System.out.println("该英雄已经存在,不能重复添加");
        } else {
            node.next = temp.next;
            temp.next = node;
        }
    }

    //删除节点
    public void delete(int no) {
        //辅助变量,用于遍历
        Node temp = head;
        //查找节点标志,fales表示未找到要删除的节点,true表示找到要删除的节点
        boolean flag = false;
        //遍历
        while (true) {
            //找到链表最后
            if (temp.next == null) {
                break;
            }
            //找到要删除的节点
            if (no == temp.next.data) {
                flag = true;
                break;
            }
            //如果没有找到,就将temp后移
            temp = temp.next;
        }
        if (flag) {//说明已经找到要删除的节点
            temp.next = temp.next.next;
        } else {
            System.out.println("未找到该英雄");
        }
    }
    //修改链表
   public void update(Node node) {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //定义辅助变量
        Node temp = head.next;
        boolean flag = false;
        //遍历
        while (true) {
            //找到链表最后
            if (temp.next == null) {
                break;
            }
            //找到要修改的节点
            if (temp.data == node.data) {
                flag = true;
                break;
            }
            //如果没有找到,就将temp后移
            temp = temp.next;
        }
        if (flag) {
            temp.data1 = node.data1;
        } else {
            System.out.println("未找到该英雄");
        }
    }
    
    //显示链表
    public void list() {
        //判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //使用辅助变量遍历
        Node temp = head.next;
        while (true) {
            //判断是否到最后
            if (temp == null) {
                break;
            }
            //输出节点的信息
            System.out.println(temp);
            //将temp后移
            temp = temp.next;
        }

    }
}

3、写一个Demo来进行测试

public class SingleLinkedListExer {
    public static void main(String[] args) {
        SingleLinkedList2 list = new SingleLinkedList2();
        Node node1 = new Node(1,10);
        Node node2 = new Node(2,20);
        Node node3 = new Node(3,30);
        Node node4 = new Node(4,40);
//		按自定义顺序进行添加
//        list.add(node1);
//        list.add(node3);
//        list.add(node2);
//        list.add(node4);
//        System.out.println("链表为:");
//        list.list();
		//按data大小进行排序添加
        list.addByOrder(node1);
        list.addByOrder(node3);
        list.addByOrder(node2);
        list.addByOrder(node4);
        System.out.println("原来链表为:");
        list.list();

        Node node5 = new Node(3,50);
        list.update(node5);
        System.out.println("修改后链表为:");
        list.list();

        list.delete(2);
        System.out.println("删除后链表为:");
        list.list();

    }
}

测试结果:
在这里插入图片描述

三、常用操作

这里要用到head节点,所以我们在SingleLinkedList2类中加上:
public Node getHead() {
return head;
}
来获取头节点

1、求单链表中有效节点的个数

这里没有另外定义类来写,直接写到public类的方法里,所以使用static修饰,下同。

/**
     *获取单链表有效节点的个数(如果带头节点,需不计头节点)
     * @param head 链表的头节点
     * @return  有效节点的个数
     */
    public static int getLength(Node head) {
        if (head.next == null) {
            return 0;
        }
        int length = 0;
        Node cur = head.next;
        while (cur != null) {
            length++;
            cur = cur.next;
        }
        return length;
    }

2、查找单链表中的倒数第k个节点

/**
     *
     * @param head 头节点
     * @param index 倒数第index个
     * @return 倒数第index个节点
     */
    public static Node getIndex(Node head, int index) {
        if (head.next == null) {
            return null;
        }
        int size = getLength(head);
        if (index <= 0 || index > size) {
            return null;
        }
        Node temp = head.next;
        for (int i = 0; i < size - index; i++) {
            temp = temp.next;
        }
        return temp;
    }

3、单链表的反转

/**
     * 将单链表反转
     * @param head
     */
    public static void reverseLinkedList(Node head) {
        if (head.next == null || head.next.next == null) {//为空或只有一个节点
            return;
        }
        //辅助节点,用来遍历原来的链表
        Node cur = head.next;
        Node next = null;//指向cur的下一个节点
        Node reverseHead = new Node();
        //遍历原来的链表
        while (cur != null) {
            next = cur.next;//暂时保存当前节点的下一个节点
            cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的头部
            reverseHead.next = cur;
            cur = next;
        }
        head.next = reverseHead.next;
    }

4、逆序打印单链表

/**
     * 逆序打印单链表
     * @param head
     */
    public static void reversePoint(Node head) {
        if (head.next == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        Node cur = head.next;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        //打印栈中节点
        while (stack.size() > 0) {
            System.out.println(stack.pop());
        }
    }

5、测试方法

public class SingleLinkedListExer {
    public static void main(String[] args) {
        SingleLinkedList2 list = new SingleLinkedList2();
        Node node1 = new Node(1,10);
        Node node2 = new Node(2,20);
        Node node3 = new Node(3,30);
        Node node4 = new Node(4,40);

        list.addByOrder(node1);
        list.addByOrder(node3);
        list.addByOrder(node2);
        list.addByOrder(node4);
        System.out.println("原来链表为:");
        list.list();
        
        System.out.println("有效节点个数为:" + getLength(list.getHead()));

        int index = 2;
        System.out.printf("倒数第%d个节点是:%s\n",index,getIndex(list.getHead(), index));

        reverseLinkedList(list.getHead());
        System.out.println("反转后的链表为:");
        list.list();

        System.out.println("逆序打印链表:");
        reversePoint(list.getHead());

    }

方法测试结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值