算法-链表

算法-链表

一、概述
线性表就是数据排成一条线一样的结构. 对不对, 你第一个想到的是什么结构.

  • 数组
  • 链表
  • 队列

非线性表:

  • 二叉树

二、数组

数组是一组连续内存空间存储的具有相同类型的数据,整个排列像一条线一样,是一种线性表数据结构。

数组的下标为什么要从 0 开始?

数组之所以广泛使用,是因为它支持随机访问。

  • 顺序访问:链表在内存中不是按顺序存放的,而是通过指针连在一起,访问某一元素,必须从链头开始顺着指针才能找到某一个元素。
  • 随机访问:数据在内存中都是按顺序存放的,通过下标直接触达到某一个元素存放的位置,随机访问,一定要满足两个条件:
    • 连续的内存空间
    • 相同类型的数据

公式:

int[n] = base_address + n * data_size
  • base_address,表示数组的首地址
  • n,表示偏移量
  • data_size,表示数组类型的字节数

假如将数组的首个下标从 1 开始 ,读取 下标为n 的数据公式:

int[n] =   base_address + (n-1* data_size

与上面的公式的区别,多了一次 n-1 操作

虽然也能读取数组中的值,但是多了一次减法的指令运算。数组是一个最基础、最简单的数据结构。要知道上层API内部很多都会依赖于数组,而互联网应用又讲究一个高并发,如果千万级QPS,如此高频的访问量,这个冗余的减运算 就会放大无数倍,产生巨大的性能损耗。所以要从 0 开始

三、链表

链表:是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的

双向链表(double linked list):双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针。

四、翻转链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
      if (head == null) {
         return null;
      }
        // 1 -> 2 -> 3 -> 4 -> 5
        //prev = 1
        // current = 2;
      ListNode prev = head;
      ListNode current = head.next;
        // 1  2 -> 3 -> 4 -> 5
        // prev = 1
        // current = 2;
      prev.next = null;
      while (current != null) {
          // First time
          /*
          next = 3
          1  <- 2  3 -> 4 -> 5
          prev = 2
          current = 3
          */
          // Second time
          /*
          next = 4
          1  <- 2 <- 3  4 -> 5
          prev = 3
          current = 4
          */
          // third time
          /*
          next = 5
          1  <- 2 <- 3 <- 4 5
          prev = 4
          current = 5
          */
          // fourth time
          /*
          next = null
          1  <- 2 <- 3 <- 4 <-5
          prev = 5
          current = null
          */
          ListNode next = current.next;
          current.next = prev;
          prev = current;
          current = next;
      }
        //exit
        //return prev = 5
      return prev;
    }
}

五、翻转链表2(指定区域内链表翻转)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if (head == null || m >= n) {
            return head;
        }
        ListNode dummy = new ListNode(-1);
        //增加一个前置节点
        dummy.next = head;
        head = dummy;
        //m 位置设置为head节点
        for (int i = 1; i < m; i++) {
            head = head.next;
        }
        ListNode prevM = head;
        ListNode mNode = head.next;
        ListNode nNode = mNode;
        ListNode postN = nNode.next;
        //区域内翻转
        // nNode => prev;
        //postN => current
        for(int i = m; i < n; i++) {
            /**
            ListNode next = current.next;
            current.next = prev;
            prev = current;
            current = next;
            **/
            ListNode next = postN.next;
            postN.next = nNode;
            nNode = postN;
            postN = next;
        }
        //调整整个链表
        mNode.next = postN;
        prevM.next = nNode;
        return dummy.next;
    }
}

六、深度拷贝带随机指针的链表

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

 public Node copyRandomList(Node head) {
        if(head == null) {
            return null;
        }
        Map<Node, Node> map = new HashMap<Node, Node>();
        Node newHead = head;
        //先深度拷贝node 节点,使用HashMap
        while (newHead != null) {
            if (!map.containsKey(newHead)) {
                Node node = new Node(newHead.val);
                map.put(newHead, node);
            }
            if (newHead.random != null) {
                Node random = newHead.random;
                if (!map.containsKey(random)) {
                    Node copyRandom = new Node(random.val);
                    map.put(random, copyRandom);
                }
                map.get(newHead).random = map.get(random);
            }
            newHead = newHead.next;
        }

        //从头开始,从HashMap拿出弄得,组装新链表
        newHead = head;
        while (newHead != null) {
            Node next = newHead.next;
            map.get(newHead).next = map.get(next);
            newHead = newHead.next;
        }
        return map.get(head);
    } 

解法2:

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

class Solution2 {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        copy(head);
        copyRandom(head);
        return split(head);
    }
    public void copy(Node head) {
        Node node = head;
        while(node != null) {
            Node copy = new Node(node.val);
            copy.next = node.next;
            node.next = copy;
            node = copy.next;
        }
    }
    
    public void copyRandom(Node head) {
        Node node = head;
        while(node != null && node.next != null) {
            if (node.random != null) {
                node.next.random = node.random.next;
            }
            node = node.next.next;
        }
    }
    
    public Node split(Node head) {
        Node result = head.next;
        Node move = head.next;
        while(head != null && head.next != null) {
            head.next = head.next.next;
            head = head.next;
            if (move != null && move.next != null) {
                move.next = move.next.next;
                move = move.next;
            }
        }
        return result;
    }
}

7、链表相加(值相加)


/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        //进位值
        int carry = 0;
        //新链表
        ListNode head = new ListNode(-1);
        ListNode pre = head;
        while (l1 != null && l2 != null) {
            int number = l1.val + l2.val + carry;
            carry = number / 10;
            ListNode node = new ListNode(number % 10);
            //组装新链表
            pre.next = node;
            pre = pre.next;
            l1 = l1.next;
            l2 = l2.next;
        }
        //如果l1 还有值
        while (l1 != null) {
            int number = l1.val + carry;
            carry = number / 10;
            ListNode node = new ListNode(number % 10);
            pre.next = node;
            pre = pre.next;
            l1 = l1.next;
        }
        
         //如果l2 还有值
        while (l2 != null) {
            int number = l2.val + carry;
            carry = number / 10;
            ListNode node = new ListNode(number % 10);
            pre.next = node;
            pre = pre.next;
            l2 = l2.next;
        }
        //链表相加结束,最后的值相加有进位
        if (carry != 0) {
            ListNode node = new ListNode(carry);
            pre.next = node;
        }
        return head.next;
    }
}

八、手写LRU缓存

class LRUCache {
    private class CacheNode {
        CacheNode prev;
        CacheNode next;
        int key;
        int value;
        public CacheNode(int key, int value) {
            this.key = key;
            this.value = value;
            this.prev = null;
            this.next = null;
        }
    }
    
    private int capacity;
    private Map<Integer, CacheNode> valNodeMap = new HashMap();
    private CacheNode head = new CacheNode(-1, -1);
    private CacheNode tail = new CacheNode(-1, -1);

    public LRUCache(int capacity) {
        this.capacity = capacity;
        tail.prev = head;
        head.next = tail;
    }
    
    public int get(int key) {
        if (!valNodeMap.containsKey(key)) {
            return -1;
        }
        CacheNode current = valNodeMap.get(key);
        current.prev.next = current.next;
        current.next.prev = current.prev;
        moveToTail(current);
        return valNodeMap.get(key).value;
    }
    
    public void put(int key, int value) {
        if (get(key) != -1) {
            valNodeMap.get(key).value = value;
            return;
        }
        
        if (valNodeMap.size() == capacity) {
            valNodeMap.remove(head.next.key);
            head.next = head.next.next;
            head.next.prev = head;
        }
    
        CacheNode insert = new CacheNode(key, value);
        valNodeMap.put(key, insert);
        moveToTail(insert);
    }
    
    private void moveToTail(CacheNode current) {
        current.prev = tail.prev;
        tail.prev = current;
        current.prev.next = current;
        current.next = tail;
    }
}

 

解法2:LinkdHashMap实现LRU算法

    //存放入的key和value
    LinkedHashMap<Integer, Integer> cache;
    int cap;
    public LRUCache(int capacity) {
        cache = new LinkedHashMap<>();
        cap = capacity;
    }
    
    public int get(int key) {
        if(!cache.containsKey(key)) {
            return -1;
        }
        //访问后,要把这个key-value Entry变为最近访问的,即放到链表的最后面
        int value = cache.get(key);
        cache.remove(key);
        cache.put(key, value);
        return value;
    }
    
    public void put(int key, int value) {
        //如果之前存在相同的key,那么应该更新value,且变为最近访问的
        if(cache.containsKey(key)) {
            cache.remove(key);
            cache.put(key, value);
        } else {
            //如果之前不存在相同的key,那么应该放到最后面,
            //但是放之前需要先看看容量是否够,如果已满,需要淘汰最近最久未访问的(链表头部)
            if(cache.size() == cap) {
                int firstKey = cache.keySet().iterator().next();
                cache.remove(firstKey);        
            }
            cache.put(key, value);
        }
    }

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值