数据结构-栈&队列&单向链表

本文深入讲解了栈、队列及链表三种基本数据结构的特点与应用,并提供了详细的代码实例,包括利用栈实现快速排序的非递归算法、链表的基本操作及合并两个有序链表的算法。

栈(Stack)是一种先进后出的数据结构。栈对于数据的操作只能从栈顶进行操作,新放入的数据都位于栈顶,也就是第一的位置,以前放入的就会被向下压,所以数据入栈也叫做压栈

栈
出栈的形式与入栈类似,每次都只能获取栈顶的元素。如我们要取得“200”就必须先取出“300”,然后再取一次才能成功取得“200”.

public class Test01_Stack {
    public static void main(String[] args) {

        String[] data = {"a","b","c","d","e"};
        Stack<String> stack = new Stack<String>();
        for(String s:data){
            stack.push(s);// 入栈的方法
        }

        for(int i=0;i<data.length;i++){
            String s = stack.pop();// 出栈的方法
            System.out.println(s);// e d c b a
        }// Stack类还有其他三个常用操作方法,可以通过查询API了解其使用方法
    }
}
  • 使用栈的先进后出特性实现快速排序的非递归算法
public class Test03_quickSortNoRec {
    public static void main(String[] args) {
        int[] a = new int[20];
        Random random = new Random();
        for(int i=0;i<a.length;i++){
            a[i] = random.nextInt(101);
        }// 产生随机数组
        System.out.println(Arrays.toString(a));

        quickSort(a);// 排序
        System.out.println(Arrays.toString(a));
    }

    private static void quickSort(int[] a) {
        Stack<Node> stack = new Stack<>();// 创建栈对象

        stack.push(new Node(0,a.length-1));// 压栈,获得第一次排序的边界

        while(!stack.empty()){// 当栈中空时,相当于所有的子递归全部实现
            Node node = stack.pop();//出栈,取得当前排序的边界,相当于一次递归
            int left = node.left;
            int right = node.right;

            boolean isRight = false;// 快速排序算法代码
            // 降序排序
            while(left < right){
                if(a[left] < a[right]){
                    int t = a[left];
                    a[left] = a[right];
                    a[right] = t;
                    isRight = !isRight;
                }
                if(isRight){
                    right--;
                }else{
                    left++;
                }
            }
            // 通过压栈,实现类似递归的功能
            if(right+1 < node.right){
                stack.push(new Node(right+1,node.right));
            }
            if(left-1 > node.left){
                stack.push(new Node(node.left,left-1));
            }
        }
    }

}

class Node{// 封装类,存储一次排序的左右边界
    public int left;
    public int right;
    public Node(int left, int right) {
        super();
        this.left = left;
        this.right = right;
    }

}

队列

队列是特殊的线性表,只允许在表的前端删除元素,在表的后端插入元素。所以队列是先进先出的数据结构。

  • 为什么要使用队列:
    • 计算机的任务调度系统
    • 为了削减高峰时期订单请求,使用消息队列
    • 其它数据结构比如树的广度优先遍历也需要借助队列来实现
    • Android中用于实现线程间通信的消息队列是队列的典型应用之一
// 自定义类实现队列特性,在Java中,可以将LinkedList类用作队列
public class MyQueue<E> {
    ArrayList<E> data = new ArrayList<E>();

    public void push(E value){
        data.add(value);
    }

    public E pop(){
        E pop = data.remove(0);
        return pop;
    }

    public boolean isEmpty(){
        return data.size()==0;
    }

    public E peek(){
        E peek = data.get(0);
        return peek;
    }

    public int size(){
        return data.size();
    }
}

链表(单向链表)

线性表是最基本、最简单、也最常用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。
一个链表是由节点组成的,类似于火车一样。每个链表(火车)有n个节点(车厢),一个节点内(车厢内)一部分内存(空间)用来存储数据值(乘客),还有一部分内存用来存储指向下一个节点的地址(通向下一节车厢的门的钥匙),我们通过访问这个地址(钥匙)就可以得到下一个节点(到达下一节车厢)。

// 自定义类实现链表的模拟
public class Test06_LinkedList {
    public static void main(String[] args) {
    // 一个MyNode对象就是一个节点
        MyNode<String> node = new MyNode<>("a");// 第一个节点
        MyNode<String> head = node;// 头节点,火车头

        // 第一个节点的next存放指向第二个节点的地址(钥匙)
        node.next = new MyNode<>("b");
        // 通过钥匙进入下一车厢
        node = node.next;
        // 第二个节点的next存放指向第三个节点的地址(钥匙)
        node.next = new MyNode<>("c");

        node = node.next;

        // 回到头节点
        node = head;
        // 遍历链表
        while(node!=null){
            String str = node.value;// 取得节点存储的数据值
            System.out.println(str);
            node = node.next;// 跳向下一个节点
        }
    }
}

节点类

public class MyNode<T> {
    public T value;// 节点中存储的数据值
    public MyNode<T> next;// 节点中存储的指向下一个节点的引用(钥匙)
    public MyNode(T value) {
        super();
        this.value = value;
    }

    @Override
    public String toString() {
        return value.toString();
    }
}
  • 链表中删除节点
public class Test07_LinkedList2 {
    public static void main(String[] args) {
        String[] data = {"a","b","c","d","e"};
        MyNode<String> node = new MyNode<String>(data[0]);
        MyNode<String> head = node;
        for(int i=1;i<data.length;i++){
            node.next = new MyNode<String>(data[i]);
            node = node.next;
        }

        Scanner scanner = new Scanner(System.in);
        int select;
        do{
            System.out.println("1-删除首节点");
            System.out.println("2-删除尾节点");
            System.out.println("3-删除值为c的节点");
            System.out.println("4-退出");
            select = scanner.nextInt();
            switch(select){
            case 1:
                head = deleteFirstNode(head);// 删除首节点
                myPrint(head);
                break;
            case 2:
                head = deleteLastNode(head);// 删除尾节点
                myPrint(head);
                break;
            case 3:
                try {
                    head = deleteKeyNode(head,"e");// 删除指定值的节点
                    myPrint(head);
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
                break;
            }
        }while(select!=4);// 退出条件

        scanner.close();
    }

    /**
     * 删除指定值的节点
     * @param head
     * @throws Exception 
     */
    private static MyNode<String> deleteKeyNode(MyNode<String> head, String key) throws Exception {
        if(head == null){
            throw new Exception("链表不能为空");
        }
        if(head.value.equals(key)){// 删除首节点的情况
            head = deleteFirstNode(head);
            return head;
        }

        MyNode<String> node = head.next;
        MyNode<String> preNode = head;

        while(node.next!=null){
            if(node.value.equals(key)){// 删除中间节点的情况
                preNode.next = node.next;
                break;
            }
            preNode = node;
            node = node.next;
        }
        if(node.value.equals(key)){// 删除尾节点的情况
            preNode.next = null;
            return head;
        }
        if(node.next==null){
            throw new Exception("删除节点不存在");
        }
        return head;
    }

    /**
     * 遍历链表,打印信息
     * @param node
     */
    private static void myPrint(MyNode<String> node) {
        while(node!=null){
            System.out.println(node.value);
            node = node.next;
        }
    }

    /**
     * 删除尾节点
     * @param head
     */
    private static MyNode<String> deleteLastNode(MyNode<String> head) {
        if(head==null || head.next==null){
            return null;
        }
        MyNode<String> result = head;
        while(head.next.next != null){
            head = head.next;
        }// 找到倒数第二个节点
        head.next = null;// 删除最后一个节点
        return result;
    }

    /**
     * 删除首节点
     * @param head
     * @return
     */
    private static MyNode<String> deleteFirstNode(MyNode<String> head) {
        if(head==null){
            return null;
        }
        head = head.next;
        return head;
    }
}

练习

2.合并两个有序的链表,合并的链表仍然有序。
解题思路:
node1={1,3,5,7,9,12}
node2={2,4,6,8,10,11,13};
外循环遍历node2链表
内循环遍历node1链表,每次判断node2是否能插入到node1链表中,
不能则node1遍历至下一个节点。
能则退出内循环,将node2插入到node1链表中。
node2遍历至下一个节点。

public class HomeWork01 {
    public static void main(String[] args) {
        int[] arr1 = {1,3,5,7,9,12};
        int[] arr2 = {2,4,6,8,10,11,13};
        MyNode<Integer> head1 = createLinkedList(arr1);// 创建第一个链表
        MyNode<Integer> head2 = createLinkedList(arr2);// 创建第二个链表

        merge(head1, head2);
        myPrint(head1);

    }

    /**
     * 合并链表
     * @param head1    被插入的目标链表
     * @param head2    用来插入的数据链表
     */
    private static void merge(MyNode<Integer> head1, MyNode<Integer> head2) {
        while(head2!=null){
            MyNode<Integer> node1=head1;// 获得被插入链表的首节点
            int t = head2.value;// 获得要插入的链表的对应节点数据
            MyNode<Integer> preNode=null;
            while(node1!=null){
                if(t <= node1.value){// 升序排序
                    break;
                }
                preNode = node1;
                node1=node1.next;
            }
            MyNode<Integer> insert = new MyNode<>(t);// 通过要插入的数据创建新的节点
            preNode.next = insert;// 将新的节点插入到目标链表中
            insert.next = node1;

            head2 = head2.next;// 遍历数据链表,获得下一个需要插入的值
        }
    }


    /**
     * 遍历链表,打印信息
     * @param head1
     */
    private static void myPrint(MyNode<Integer> head1) {
        MyNode<Integer> node = head1;
        while(node!=null){
            System.out.print(node.value + " ");
            node = node.next;
        }

    }

    /**
     * 创建链表
     * @param arr1
     * @return
     */
    private static MyNode<Integer> createLinkedList(int[] arr1) {
        MyNode<Integer> node = new MyNode<Integer>(arr1[0]);
        MyNode<Integer> head = node;
        for(int i=1;i<arr1.length;i++){
            node.next = new MyNode<Integer>(arr1[i]);
            node = node.next;
        }
        return head;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值