草庐IT

【A】1.2 链表

安如衫 2023-03-28 原文

和数组不同的是,链表并不需要一块连续的内存空间,它可以通过指针将一组零散的内存块串联起来使用。链表的基本操作是最考验逻辑思维能力的,尤其需要注意:

  1. 浅拷贝相当于引用
  2. 哨兵/保护结点作为访问入口
  3. 边界与异常检查
刷题清单 备注
21. 合并两个有序链表
203. 移除链表元素
206.反转链表
707.设计链表
*25.K 个一组翻转链表 保护结点
24. 两两交换链表中的节点
141.环形链表 快慢指针|哈希表
*142.环形链表 II 快慢指针|哈希表|数学
*19. 删除链表的倒数第 N 个结点 快慢指针

AC136.邻值查找

对序列\(A\)中每一个数\(A_i\),求在\(A_i\)前的数与\(A_i\)的最小差值,以及对应下标(若最小差值对应下标不唯一,则返回最小下标)。例如,输入1, 5, 3, 4, 2

treemap

一个可能的想法是,对前\(i\)个数维护一个有序集合,二分查找\(A_i\)的前驱与后继,比较差值,返回最小差值与对应下标:

1, 5:添加5,前驱为1,后继为\,最小差值为4,对应下标1
1,3,5:添加3,前驱为1,后继为5,最小差值为2,对应下标1
1,3,4,5:添加4,前驱为3,后继为5,最小差值为1,对应下标2
1,2,3,4,5:添加2,前驱为1,后继为3,最小差值为1,对应下标1

import java.io.*;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) throws Exception {
        StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        BufferedWriter br = new BufferedWriter(new OutputStreamWriter(System.out));

        st.nextToken();
        int n = (int) st.nval;

        // 平衡树
        TreeMap<Integer, Integer> map = new TreeMap<>();
        for (int i = 1; i <= n; i++) {
            st.nextToken();
            int val = (int) st.nval;
            // 添加关键字和对应的值,即数值与下标
            map.put(val, i);
            if (i != 1) {
                // 返回大于等于 key 的最小元素
                Map.Entry<Integer, Integer> up = map.ceilingEntry(val + 1);
                // 返回小于等于 key 的最大元素
                Map.Entry<Integer, Integer> down = map.floorEntry(val - 1);
                int res = Integer.MAX_VALUE, pos = -1;
                if (down != null) {
                    res = val - down.getKey();
                    pos = down.getValue();
                }
                if (up != null && up.getKey() - val < res) {
                    res = up.getKey() - val;
                    pos = up.getValue();
                }
                br.write(res + " " + pos);
                br.newLine();
            }
        }
        br.flush();
    }
}

doubly-linked list

顺序添加元素可行,那倒序删除呢?依然是维护一个有序序列:

图1.2.1 样例及实现


  • 数组:按下标索引,记录每个下标对应的链表结点,便于快速访问
  • 双链表:按值排序,倒序考虑每个下标,前后比较,删除相应结点

注意,虽然Java内置LinkedList,但是容器接口没有暴露具体的实现方法,也无法获取前驱后继。
所以,双链表需要自己手动实现。

import java.io.*;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;


class Main {
    static int n;
    // 双链表,便于删除和查询前驱后继
    static Node head;
    static Node tail;
    // 数组,便于根据索引快速访问结点


    public static void main(String[] args) throws IOException {
        StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        st.nextToken();
        n = (int) st.nval;
        Node[] pile;
        pile = new Node[n + 1];

        // 虚拟/保护结点
        head = new Node(-1, -1);
        tail = new Node(-1, -1);
        // 空双链表初始化
        head.next = tail;
        tail.prev = head;

        // 按值排序
        Node[] nodes = new Node[n];
        for (int i = 1; i <= n; i++) {
            st.nextToken();
            Node node = new Node((int) st.nval, i);
            nodes[i - 1] = node;
            pile[i] = node;
        }
        Arrays.sort(nodes, (o1, o2) -> o1.val - o2.val);
        for (int i = n - 1; i >= 0; i--) {
            // 头插
            Node node = nodes[i];
            node.next = head.next;
            node.prev = head;
            head.next.prev = node;
            head.next = node;
        }

        Deque<String> stack = new ArrayDeque<>();
        for (int i = n; i >= 2; i--) {
            Node node = pile[i];

            int pos = -1;
            int diff = Integer.MAX_VALUE;

            // 优先考虑前驱
            if (node.prev != head) {
                diff = node.val - node.prev.val;
                pos = node.prev.index;
            }
            if (node.next != tail && node.next.val - node.val < diff) {
                diff = node.next.val - node.val;
                pos = node.next.index;
            }

            stack.push(diff + " " + pos);

            // 删除结点
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }

        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        while (!stack.isEmpty()) {
            bw.write(stack.pop());
            bw.newLine();
        }
        bw.flush();
        bw.close();
    }

    static class Node {
        int val;
        int index;
        Node prev;
        Node next;

        public Node(int val, int index) {
            this.val = val;
            this.index = index;
        }
    }
}

有关【A】1.2 链表的更多相关文章

  1. ruby - 名称错误 : undefined - have parsing rules for local variables changed in Ruby 2. 1.2? - 2

    我得到NameError:undefinedlocalvariableormethodwithruby​​2.1.2正如在thisquestion中观察到的那样,表达式如:barifbar=true引发未定义的局部变量错误(前提是bar之前未定义),因为bar在分配之前被解析器读取。而且我相信以前用这个表达式没有什么区别:barifbar=false两者之间的区别在于主体是否被求值,但如果遇到未定义的局部变量会在求值条件之前立即引发错误,那应该无关紧要。但是当我在Ruby2.1.2上运行第二个代码时,它没有引发错误。以前也是这样吗?如果是这样,那么解析讨论的内容是什么?如果没有,Rub

  2. 【数据结构和算法】实现带头双向循环链表(最复杂的链表) - 2

    前文,我们实现了认识了链表这一结构,并实现了无头单向非循环链表,接下来我们实现另一种常用的链表结构,带头双向循环链表。如有仍不了解单向链表的,请看这一篇文章(7条消息)【数据结构和算法】认识线性表中的链表,并实现单向链表_小王学代码的博客-CSDN博客目录前言一、带头双向循环链表是什么?二、实现带头双向循环链表1.结构体和要实现函数2.初始化和打印链表3.头插和尾插4.头删和尾删5.查找和返回结点个数6.在pos位置之前插入结点7.删除指定pos结点8.摧毁链表三、完整代码1.DSLinkList.h2.DSLinkList.c3.test.c总结前言带头双向循环链表,是链表中最为复杂的一种结

  3. ruby-on-rails - 在 ruby​​/rails 中将 1200 转换为 1.2K - 2

    我认为ruby​​或rails中有一种方法可以执行此操作,但我不记得在哪里可以找到它或如何搜索它,所以我希望stackoverflow的集体智慧可能有所帮助。我不介意编写一个方法来执行此操作,但我确信有人有更好的解决方案。 最佳答案 number_to_human(1200,:format=>'%n%u',:units=>{:thousand=>'K'})#1200=>1.2K 关于ruby-on-rails-在ruby​​/rails中将1200转换为1.2K,我们在StackOver

  4. ruby - Ruby 有像栈、队列、链表、映射或集合这样的容器吗? - 2

    我在网上查了几个Ruby教程,他们似乎什么都用数组。那么如何在Ruby中实现以下数据结构呢?堆栈队列链表map组 最佳答案 (从评论中移出)好吧,通过限制堆栈或队列方法(push、pop、shift、unshift),数组可以是堆栈或队列。使用push/pop提供LIFO(后进先出)行为(堆栈),而使用push/shift或unshift/pop提供FIFO行为(队列)。map是hashes,和一个Set类已经存在。您可以使用类实现链表,但数组将使用标准数组方法提供类似于链表的行为。 关

  5. javascript - attrs.$set ('ngClick' , 函数名 + '()' );不再适用于 angular 1.2rc3 - 2

    我有一个开源项目,正在升级以使用angular1.2rc3。本质上它处理表单按钮上的promise。在这个plnkrhttp://plnkr.co/edit/vQd97YEpYO20YHSuHnN0?p=preview您应该能够单击右侧的“保存”并在控制台中看到“已单击”,因为它应该在指令中执行此代码:scope[functionName]=function(){console.log('clicked');//ifit'salreadybusy,don'tacceptanewclickif(scope.busy===true){return;}scope.busy=true;varr

  6. javascript - JS将数组转换为json链表? - 2

    我是JS的新手,组织数据的概念让我有些困惑,我试图从特定的数组格式中获取数据(因为这是我必须使用的格式)并将其输出为另一种特定的JSON格式。这是给D3sankey模块传递数据https://github.com/d3/d3-plugins/blob/master/sankey/sankey.js我不知道如何将节点的索引添加到链接中,而不是名称。真的,我完全迷失了它!我在这里做了一个fiddle:https://jsfiddle.net/adamdavi3s/kw3jtzx4/下面是所需数据和输出的示例vardata=[{"source":"Agricultural'waste'","

  7. 合并两个有序链表 - 2

    文章目录1.题目描述2.解题思路方法1:方法2:1.题目描述题目链接:力扣21,合并两个有序链表将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。2.解题思路方法1:首先我们能够想到的就是遍历一遍数组,判断两个结点的大小,将数值小的结点放在前面,数值大的不断尾插在后面。是不是听着挺简单的?具体实现:我们可以创建两个空指针,head用来存放链表的头结点,tail用来遍历两条链表,将两条链表链接起来。当某个链表为空时,我们可以直接返回另一条链表当两个链表都不为空时,我们可以不断比较两条链表的大小,当head和tail为空时,我们将较小的结点同时赋给head

  8. javascript - Javascript 中的链表与数组 - 2

    所以我在JS中玩弄链表并提出了以下问题:比方说,我们有一个数组和一个链表,它们都有5000个元素。我们想在索引10处插入新元素。数组方式非常简单。我们在给定索引处插入新元素,并将其余元素向前移动一个索引。所以我尝试用链表来做这件事,并以下面的方式结束它:从NicholasZakas获取链表的实现并添加附加方法addOnPosition(data,index)。最后是代码:functionLinkedList(){this._head=null;this._length=0;}LinkedList.prototype={constructor:LinkedList,add:functio

  9. javascript - AngularJS 1.2 中的随机 orderBy 返回 'infdig' 错误 - 2

    在thisquestion中使用随机orderBy排序技术在AngularJS1.1中工作正常。varmyApp=angular.module('myApp',[]);functionMyCtrl($scope){$scope.list=['a','b','c','d','e','f','g'];$scope.random=function(){return0.5-Math.random();}}但是,在1.2中,它会将infdig错误放入控制台,并且需要更长的时间来返回排序结果:http://jsfiddle.net/mblase75/jVs27/控制台中的错误如下所示:Error:

  10. javascript - 创建一个新选项并使用 mootools 1.2 注入(inject)选择框 - 2

    我有一个返回国家列表的AJAX函数。它工作正常。我的问题是,想要在已经是HTML且为空的选择框中加载国家/地区意味着其中没有选项值。我想知道如何使用moo工具1.2创建一个新的选项元素并注入(inject)到选择框中我使用了下面的代码,但它在IE中不起作用。varNewOption=newOption("SelectSubCategory",'0');NewOption.inject($('nSub_nIndustryID'))谢谢阿维纳什 最佳答案 没有必要创建变量,除非您稍后要使用它。newElement('option',{'

随机推荐