数据结构:单链表增删改查的思路和代码实现

链表是以节点的方式来存储的。
1.一个节点包含。 data域(用来存放数据),next域:指向下一个节点(存储的是内存的地址)。
2.各个节点不一定是连续存放的
3.链表分带头结点的链表和没有头节点的链表。

import java.util.Stack;

public class MyLinkedList {
    //定义一个头节点。不存放具体的数据。
    private User head = new User(0, "");

    public User getHead() {
        return head;
    }
    //添加节点到单向链表。

    /**
     * 默认排序添加思路:
     * 1.找到当前链表的最后节点。
     * 2.将最后节点的next指向新的节点
     */
    public void add(User user) {
        //因为head节点不可以动,因此需要创建一个辅助节点
        User temp = head;
        //循环查找。找到链表的最后
        while (true) {
            //找到链表的最后
            if (temp.next == null) {
                break;
            }
            //如果没有找到最后,将temp后移
            temp = temp.next;
        }
        //把新的节点指向链表最后
        temp.next = user;
    }

    /**
     * 按照ID排序添加思路:
     * 1.通过遍历找到新添加的节点的位置,
     * 2.把新的节点.next 指向 temp.next
     * 3.把temp.next指向新的节点。
     */
    public void addByOrder(User user) {
        //因为头节点不能动。因此需要通过一个辅助指针来帮助查找添加的位置。
        //我们找的temp应该是位于添加位置的前一个节点。否则无法插入。
        User temp = head;
        while (true) {
            //如果为null说明已经到最后了。这时候直接插入就可以了。
            if (temp.next == null) {
                break;
            }
            //如果当前节点的下一个节点的ID比新节点的ID大,则表示找到(新节点在这两个中间插入。)
            if (temp.next.id > user.id) {
                break;
            } else if (temp.next.id == user.id) {
                //如果编号存在 直接返回。不做插入操作
                System.out.printf("编号%d存在了。无法添加。", user.id);
                return;
            }
            temp = temp.next;
        }
        //.把新的节点.next 指向 temp.next
        user.next = temp.next;
        //把temp.next指向新的节点。
        temp.next = user;
    }

    /**
     * 修改节点思路:
     * 1.找到和新节点相同ID的节点。 temp.id == 新节点.id
     * 2.更改姓名 temp.name = 新节点.name;
     */
    //修改节点信息。根据人物ID修改人物名称。
    public void update(User newUser) {
        //判空
        if (head.next == null) {
            System.out.println("链表为空。无法修改");
            return;
        }
        //找到需要修改的节点,根据人物ID
        //因为头节点不能动。所以定义一个辅助节点
        User temp = head.next;
        while (true) {
            if (temp == null) {
                break;
            }
            //如果找到则直接修改名字
            if (temp.id == newUser.id) {
                temp.name = newUser.name;
                return;
            }
            //temp后移实现遍历
            temp = temp.next;
        }
        System.out.printf("没有找到ID为%d的节点,无法修改", newUser.id);
    }

    /**
     * 删除节点思路:(根据人物ID来删除)
     * 1.先找到和要删除节点相同ID的前一个节点 temp.
     * 2.temp.next = temp.next.next (上一个节点直接指向当前节点的下一个节点。)
     * 3.被删除的节点,将不会有其他引用指向,会被垃圾回收机制回收。
     */
    public void delete(int id) {
        User temp = head;
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                break;
            }
            //找到了待删除节点的前一个节点temp。
            if (temp.next.id == id) {
                //待删除的上一个节点指向他的下一个节点(实现删除。)
                temp.next = temp.next.next;
                return;
            }
            //temp后移实现遍历
            temp = temp.next;
        }
        System.out.printf("要删除的节点%d不存在", id);
    }

    /**
     * 打印输出思路:
     * 1.找到头节点的下一个节点。指向辅助节点temp
     * 2.打印输出temp并将temp指向下一个节点。
     * 3.循环第二步。
     */
    //显示当前链表。
    public void showList() {
        //判断当前链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        //因为头节点不能动。因此需要一个辅助节点。
        User temp = head.next;
        while (true) {
            //判断是否到最后
            if (temp == null) {
                break;
            }
            //输出节点信息
            System.out.println(temp);
            //temp后移实现遍历
            temp = temp.next;
        }
    }

    /**
     * 面试题:获取单链表有效节点的个数(忽略头节点。)
     */

    public static int getLength(User head) {
        if (head.next == null) {
            return 0;
        }
        int length = 0;
        User temp = head.next;
        while (temp != null) {
            length++;
            temp = temp.next;
        }
        return length;
    }

    /**
     * 面试题:查找单链表中的倒数第N个节点
     * 思路:
     * 1.编写一个方法接受 单链表的head节点和index。
     * index表示是倒数第index个节点
     * 2.获取链表的长度length。
     * 3.得到length后,从链表第一个开始遍历(lenth-index)个就是需要的节点。
     * 4.如果找到返回该节点。找不到返回null
     */
    public static User findLastIndexNode(User head, int index) {
        //如果链表为空返回null
        if (head.next == null) {
            return null;
        }
        //获取链表的长度。调用上一个方法。
        int length = getLength(head);
        //遍历 length -index位置,就是倒数第index的节点。
        if (index <= 0 || index > length) {
            return null;
        }
        //定义给辅助变量,for循环定位到倒数的index
        User temp = head.next;
        for (int i = 0; i < length - index; i++) {
            temp = temp.next;
        }

        return temp;
    }

    /**
     * 面试题:实现单链表的翻转。
     * 思路:
     * 1.先定义一个节点 reverseHead = new User()  reverseHead.next默认为null
     * 2.从头到尾遍历原来的链表,每遍历一个节点,就将其next指向上一个节点(第一个会指向null),
     * 然后取出放入新节点的next。
     * 3.完成遍历后。把原来链表的head.next指向新节点的next即可。
     */
    public static void reversetList(User head) {
        //如果当前链表为空,或者只有一个节点。则无需反转
        if (head.next == null || head.next.next == null) {
            return;
        }
        //定义一个辅助节点。
        User temp = head.next;
        //指向当前节点的下一个节点。
        User next = null;
        //创建一个新节点。(相当于一个容器 用来暂时存放未翻转完的链表。)
        User reverseHead = new User(0, "");
        //遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
        while (temp != null) {
            //先取出当前节点的下一个节点备用。否则设置next后会无法找到下一个节点。
            next = temp.next;
            //当前节点的next指向新节点(第一次会指向null)
            temp.next = reverseHead.next;
            //把翻转后的链表结构赋值给新节点
            reverseHead.next = temp;
            temp = next;
        }
        //最后完成翻转后。将链表的头节点指向新节点的next即可。
        head.next = reverseHead.next;
    }

    /**
     * 面试题:从尾到头打印单链表
     * 思路:
     * 1.方式1:将单链表进行反转操作,然后再遍历即可,这样做会破坏原来的单链表结构!不建议!!!
     * 2.方式2:可以利用栈这个数据结构,将各个节点压如栈中。然后利用栈的先进后出的特点。实现逆序打印效果。
     * 3.方式3:用递归打印。
     */
    //利用栈来实现逆序打印。
    public static void reversetPrint2(User head) {
        Stack<User> stack = new Stack<>();
        if (head.next==null){
            return;
        }
        User temp = head.next;
        while (temp!=null){
            stack.add(temp);
            temp = temp.next;
        }
        while (stack.size()>0){
            System.out.println(stack.pop());
        }
    }

    //利用递归来实现逆序打印。
    public static void reversetPrint3(User head) {
        if (head != null) {
            reversetPrint3(head.next);
            System.out.println(head);
        }
    }
    /**
     * 面试题:合并两个有序的单链表。合并之后链表依然有序。
     */
    public  static User  mergeList(User user1,User user2){
        if (user1==null)return user2;
        if (user2==null)return user1;
        User head = null;
        if (user1.id<user2.id){
            head =user1;
            head.next = mergeList(user1.next,user2);
        }else{
            head =user2;
            head.next = mergeList(user2.next,user1);

        }
        return  head;
    }
}



class User {
    public int id;
    public String name;
    public User next;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

class TestList {
    public static void main(String[] args) {
        User user = new User(1, "张三");
        User user1 = new User(2, "李四");
        User user2 = new User(3, "王五");
        User user3 = new User(4, "赵六");
        MyLinkedList list2 = new MyLinkedList();
        //倒序插入测试效果
        list2.addByOrder(user3);
        list2.addByOrder(user2);
        list2.addByOrder(user1);
        list2.addByOrder(user);
        list2.addByOrder(user);
        System.out.println("--------------");
        list2.showList();
        //修改节点的名字。
        User newUser = new User(1, "一号人物");

        list2.update(newUser);
        System.out.println("修改后的链表。。。");
        list2.showList();

        //删除
        // list2.delete(1);
        // list2.delete(4);
        System.out.println("删除后的链表。。。");
        list2.showList();
        //获取单链表的有效节点个数
        System.out.println(MyLinkedList.getLength(list2.getHead()));
        //获取倒数第1个节点
        System.out.println(MyLinkedList.findLastIndexNode(list2.getHead(), 1));
        //MyLinkedList.reversetList(list2.getHead());
        //System.out.println("倒序后的列表~~");
        //list2.showList();
        System.out.println("栈打印");
        MyLinkedList.reversetPrint2(list2.getHead());
        System.out.println("递归打印");
        MyLinkedList.reversetPrint3(list2.getHead().next);
        User user10 = new User(1, "张一");
        User user20 = new User(2, "李二");
        User user30 = new User(3, "王三");
        User user40 = new User(4, "赵四");
        User user50 = new User(5, "孙五");
        user10.next = user40;
        user20.next = user30;
        user30.next = user50;
        MyLinkedList list = new MyLinkedList();
       list.getHead().next =  MyLinkedList.mergeList(user10,user20);
        System.out.println("合并后的顺序");
       list.showList();
    }
}

你可能感兴趣的:(链表,java,数据结构,算法,单链表)