zoukankan      html  css  js  c++  java
  • 数据结构-数组与链表

    数组特点:
            1. 线性表,数据排成像线一样的数据结构,数据只有前后俩个方向,数组,链表,队列,栈都是线性表
            2. 连续的内存空间,让它支持随机访问,根据下标随机访问的时间复杂度为o(1),随机访问的公式
            3. 相同类型的数据结构
            4. 随机访问公式,a[i]_address = base_address + i * data_type_size,其中data_type_size为数据类型大小,eg:int四字节
    
    数组删除和插入效率低的原因
            1. 插入或者删除;都会导致后续的数据大规模移动
            2. 如果进行优化   1. 比如删除的时候,将要删除的数据和最后一位调换位置,在删除;
                             2. 或者类似jvm的标记删除发,先标记,等数组空间不够了,在真正删除,
                             3. 以上都是减少了大规模数据移动问题
    
    数组越界问题
            1. 并非所有的语言都会对数组越界进行检查,比如c,即使越界,但只要偏移计算后的内存可访问,那么程序就不会报错,所以病毒就可以利用这点进行非法访问
    
    数组与容器
            1. 拿Java来说 int[] 对应的容器是 arraylist,由于 arraylist 无法存储基础类型 ,所以需要int转化成Integer ,这个转化的就是装箱和拆箱,有一定的性能消耗
            2. arraylist,可以自动扩容,封装了很多方法,隐藏了操作细节,使用也更方便
            3. 如果仅仅是业务开发,容器就很好,那点性能消耗相较于系统整体性能无上大雅
            4. 但是如果是底层,就需要用数组,毕竟这种性能,能扣一点是一点
            5. 另外,如果刷算法题,建议用数组,这样可以更直观感受数组的变化,操作的细节。
    
    数组为什么下标从0开始
            1. 下标,也可以叫偏移,a[]中,a是首地址,a[0]代表偏移为0个size的地址
    
    链表的定义:
            1. 不同于数组,链表需要的可以不是连续的内存空间,只要总大小够即可
            2. 链表中每一个节点之间是通过指针链接
            3. 链表分为 单向链表,双向链表,双向链表
            4. 说一种简单的链表结构,如单向链表,
                ```
               -> data,next ->....;
                ```      
            5. 链表的插入和删除只需要更改一个节点
    
    数组和链表的对比
            1. 内存大小,链表每个节点需要额外的空间存储指向下一个节点的指针,多以同等数据规模下,数组内存消耗小
            2. 插入和删除的性能, 数组 o(n),链表o(1)
            3. 随机访问, 数组 o(1),链表o(n)
            4. 内存的申请上,数组需要连续的空间,链表可以是零散的空间
            5. 内存碎片,链表频繁申请释放内存,会导致内存碎片,容易频繁出发gc      
    
    leetcode上数组经典题目
    //给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复
    //的三元组。 
    //
    // 注意:答案中不可以包含重复的三元组。 
    //
    // 
    //
    // 示例: 
    //
    // 给定数组 nums = [-1, 0, 1, 2, -1, -4],
    //
    //满足要求的三元组集合为:
    //[
    //  [-1, 0, 1],
    //  [-1, -1, 2]
    //]
    // 
    // Related Topics 数组 双指针
    
    package leetcode.editor.cn;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    //Java:三数之和
    public class P15ThreeSum {
        public static void main(String[] args) {
            Solution solution = new P15ThreeSum().new Solution();
            // TO TEST
            int[] nums = {-1, 0, 1, 2, -1, -4};
            List<List<Integer>> res = solution.threeSum(nums);
            res.forEach(n -> {
                n.forEach(m -> {
                    System.out.print(m + " ");
                });
            });
        }
    
        //leetcode submit region begin(Prohibit modification and deletion)
        class Solution {
            public List<List<Integer>> threeSum(int[] nums) {
                if (nums == null || nums.length == 0) {
                    return new ArrayList<>();
                }
                Arrays.sort(nums);
                List<List<Integer>> res = new ArrayList<>();
                for (int i = 0; i < nums.length; i++) {
                    if (i == 0 || (i > 0 && nums[i] != nums[i - 1])) {
                        int l = i + 1;
                        int r = nums.length - 1;
                        while (l < r) {
                            int temp = nums[i] + nums[l] + nums[r];
                            if (temp == 0) {
                                res.add(Arrays.asList(nums[i], nums[l], nums[r]));
                                while (l < r && nums[l] == nums[l + 1]) l++;
                                while (l < r && nums[r] == nums[r - 1]) r--;
                                l++;
                                r--;
                            } else if (temp < 0) {
                                while(l<r && nums[l] == nums[l+1]) l++;
                                l++;
                            } else {
                                while(l<r && nums[r] == nums[r-1]) r--;
                                r--;
                            }
                        }
                    }
                }
                return res;
            }
        }
    //leetcode submit region end(Prohibit modification and deletion)
    
    }
    
    leetcode链表经典题目
    //合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。 
    //
    // 示例: 
    //
    // 输入:
    //[
    //  1->4->5,
    //  1->3->4,
    //  2->6
    //]
    //输出: 1->1->2->3->4->4->5->6 
    // Related Topics 堆 链表 分治算法
    
    package leetcode.editor.cn;
    
    import com.example.demo.ListNode;
    
    //Java:合并K个排序链表
    public class P23MergeKSortedLists{
        public static void main(String[] args) {
            Solution solution = new P23MergeKSortedLists().new Solution();
            // TO TEST
        }
        //leetcode submit region begin(Prohibit modification and deletion)
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    class Solution {
        public ListNode mergeKLists(ListNode[] lists) {
            ListNode res = null;
            for (int i = 0; i < lists.length; i++) {
                res =  merge(res,lists[i]);
            }
            return res;
    
        }
    
        //依次合并链表
        private ListNode merge(ListNode l1, ListNode l2){
            if(l1==null){
                return l2;
            }
    
            if(l2==null){
                return l1;
            }
    
            if(l1.val<l2.val){
               l1.next = merge(l1.next,l2);
               return l1;
            }else{
              l2.next =merge(l1,l2.next);
              return l2;
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    
    }
    
    链表还有一个经典的例子 LRU
    package com.example.demo;
    
    
    /**
     * 有序单链表实现lru
     * 思路:1.如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
     * 2.如果此数据没有在缓存链表中,又可以分为两种情况:
     * 如果此时缓存未满,则将此结点直接插入到链表的头部;
     * 如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。
     */
    public class Lc_LRU {
        public static class LinkList {
            int node;
            LinkList next;
    
            public LinkList() {
            }
    
            public LinkList(int node, LinkList next) {
                this.node = node;
                this.next = next;
            }
    
            public LinkList(int node) {
                this.node = node;
            }
    
            public int getNode() {
                return node;
            }
    
            public void setNode(int node) {
                this.node = node;
            }
    
            public LinkList getNext() {
                return next;
            }
    
            public void setNext(LinkList next) {
                this.next = next;
            }
        }
    
        //链表最大长度
        int size = 10;
    
        //遍历链表
        private boolean traversal(LinkList newNode, LinkList linkList) {
            LinkList curr = linkList;
            while (curr != null) {
                if (curr.node != newNode.node) {
                    curr = curr.next;
                } else {
                    return true;
                }
            }
            return false;
        }
    
        //遍历链表是否存在新插入的值,存在则删除
        private LinkList delete(LinkList head, LinkList toRemove) {
           if(head.node == toRemove.node){
               return head.next;
           }
           LinkList pre = head;
           LinkList curr = head;
           while(curr!=null){
               if(curr.node == toRemove.node){
                   pre.next = curr.next;
                   return pre;
               }else{
                   pre = curr;
               }
               curr = curr.next;
           }
           return null;
        }
    
        //删除最后一个节点
        private void deleteLastNode(LinkList linkList) {
            LinkList curr = linkList;
            int position = 0;
            while (curr != null && position != 8) {
                position++;
                curr = curr.next;
            }
            curr.next = null;
        }
    
    
        //遍历链表长度
        private int count(LinkList linkList) {
            LinkList curr = linkList;
            int count = 0;
            while (curr != null) {
                count++;
                curr = curr.next;
            }
            return count;
        }
    
        //插入新节点,检查是否存在该节点,若存在删除原来位置的节点,插入到头,如果不存在,直接插入到头
        private LinkList insert(LinkList newNode, LinkList linkList) {
            if (count(linkList) < 10) {
                //go on
            } else {
                deleteLastNode(linkList);
            }
            linkList = delete(linkList,newNode);
            LinkList head = newNode;
            head.next = linkList;
            return head;
        }
    
        private void sysoLinkList(LinkList linkList) {
            StringBuffer stringBuffer = new StringBuffer();
            LinkList curr = linkList;
            while (curr != null) {
                stringBuffer.append(curr.node);
                curr = curr.next;
            }
            System.out.println(stringBuffer.toString());
        }
    
        public static void main(String[] args) {
            LinkList linkList = new LinkList(2);
            LinkList newNode = new LinkList(1);
            linkList.next = newNode;
            LinkList newNode1 = new LinkList(1);
            Lc_LRU lc_lru = new Lc_LRU();
            linkList = lc_lru.insert(newNode1, linkList);
            lc_lru.sysoLinkList(linkList);
        }
    }
    
  • 相关阅读:
    深入浅出JavaScript (一)初识
    “0”基础让你学会 GridView (一)
    VS 中PageLayout 属性设置
    ASP.NET Forms身份认证
    教务系统数据库设计 (一)
    深入浅出Javascript(三)创建自定义对象以及属性、方法
    .NET 中的Cache
    深入浅出JavaScript (二) 代码放置位置与执行顺序
    触发器特殊的存储过程
    ASP.NET与JavaScript轻松实现输入信息验证
  • 原文地址:https://www.cnblogs.com/xiaoshahai/p/13398154.html
Copyright © 2011-2022 走看看