C 将内存部分移动到位

2023-12-31

我正在实现几个数据结构,我想要使用的一个原语如下:我有一个内存块 A[N] (它的长度可变,但我在示例中使用 100),在这个块内,有一个较小的部分C 长度为 K(假设为 30),我想在不使用任何额外内存的情况下移动它。

额外的困难是,A“换行”,即C可以从A[80]开始,然后C的前20个元素是元素A[80..100],最后10个元素是元素A[ 0..10]。此外,目标范围还可以以任何可能的方式“环绕”并与C重叠。此外,我不想使用超过恒定量的额外内存,一切都应该就位。另外,A 中既不在目标范围内也不在源范围内的部分可能包含重要的内容,因此也不能使用。因此,一种情况如下:

A 看起来像这样:

|456789ABCDEF0123456789AB|-----|0123|

并且应该改成这样:

|89AB|-----|0123456789ABCDEF01234567|

仅仅将其委托给库或使用库中的另一个数据结构在这里不是一个选择,我想自己理解这个问题。乍一看,我认为这可能不是什么小事,但只要你区分几个案例,就清楚了,但现在我遇到了严重的麻烦。当然,如果它们不重叠或不环绕,也会有一些微不足道的情况,但至少如果两者同时发生,就会变得混乱。您可以从一个空闲位置开始,然后移动属于该位置的部件,但是随后您在其他地方创建另一个空闲部件,并且很难跟踪您仍然可以使用哪些部件。

也许我完全遗漏了一些东西,但即使是我的特殊情况,如果目标范围没有换行,也有近 100 行(不过其中一半是断言和注释),我可以更新它,以便它还可以通过一些额外的处理处理一般情况索引计算,但如果有人有一个优雅而简短的解决方案,我将不胜感激。直觉上我认为这应该是微不足道的,但我只是还没有看到最好的解决方案。

注意:有趣的情况当然是,如果 C 几乎与 A 一样大。如果 |C|

编辑:使用超过恒定数量的附加标志/索引算作附加内存,如果可能的话我想避免这种情况。

编辑:有些人想看看我的代码。我的问题相当抽象,所以我不想发布它,但也许有人看到如何改进它。这很糟糕,它只适用于目标从头开始(但是,很容易改变)并且非常长的情况,但它在 O(n) 中不需要额外的内存就可以完成工作。

#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

void move_part(int* A, size_t N, size_t target, size_t source, size_t size, int show_steps)
{
  assert(source + size <= N);
  assert(target + size <= N);
  if (show_steps) {
    printf("Moving size %d from %d to %d.\n", size, source, target);
  }
  memmove(A + target, A + source, size * sizeof(int));
}

void swap_parts(int* A, size_t N, size_t first_begin, size_t second_begin, size_t size, int show_steps)
{
  if (show_steps) {
    printf("Swapping size %d at %d and %d.\n", size, first_begin, second_begin);
  }
  assert(first_begin + size <= N);
  assert(second_begin + size <= N);
  size_t i;
  for (i = 0; i < size; ++i) {
    int x = A[first_begin + i];
    A[first_begin + i] = A[second_begin + i];
    A[second_begin + i] = x;
  }
}

void move_to_beginning(int* A, size_t N, size_t begin, size_t size, int show_steps)
{
  assert(begin <= N);
  assert(size <= N);
  // Denotes the start of our "working range". Increases during
  // the algorithm and becomes N
  size_t part_start = 0;
  // Note: Keeping the size is crucial since begin == end could
  // mean that the range is empty or full.
  size_t end = (begin + size) % N;
  while (part_start != N) {
    size_t i;
    if (show_steps) {
      for (i = 0; i < N; ++i) {
    printf("%d ", A[i]);
      }
      printf("\n");
      printf("part_start %d  begin %d  end %d  size %d\n", part_start, begin, end, size);
    }
    // loop invariants
    assert(part_start < N);
    // The two pointers are in our range
    assert(part_start <= begin && begin <= N);
    assert(part_start <= end && end <= N);
    // size is valid (wrapped case, non-empty, non-full case)
    assert(begin <= end || (N - begin) + (end - part_start) == size);
    // size is valid (non wrapped case, non-empty, non-full case)
    assert(begin >= end || end - begin == size);
    // size is valid (working range is full or empty case)
    assert(begin != end || size == 0 || part_start + size == N);
    if (size == 0 || begin == N || begin == part_start) {
      // ##|1234|# -> 1234### ||
      if (show_steps) {
    printf("Case 1:\nTerminating\n");
      }
      // #||# -> ## ||
      // 12|##| -> 12## ||
      // |12|## -> 12## ||
      break;
      /* Not necessary any more, but would be the correct transformation:
     part_start = N;
     begin = N;
     end = N;
     size = 0;*/
    } else if (end == part_start) {
      // |##|123 -> ##|123|
      if (show_steps) {
    printf("Case 2:\n");
    printf("Setting end to %d.\n", N);
      }
      end = N;
    } else if (begin < end) {
      // ##|1234|# -> 1234### ||
      if (show_steps) {
    printf("Case 3:\n");
      }
      move_part(A, N, part_start, begin, size, show_steps);
      break;
      /* Not necessary any more, but would be the correct transformation:
     part_start = N;
     begin = N;
     end = N;
     size = 0;*/
    } else {
      size_t end_size = end - part_start;
      size_t begin_size = N - begin;
      assert(begin_size + end_size == size);
      if (end_size >= begin_size) {
    // 345|#|12 -> 12 5|#|34
    if (show_steps) {
      printf("Case 4:\n");
    }
    swap_parts(A, N, part_start, begin, begin_size, show_steps);
    assert(begin_size > 0); // Necessary for progress
    part_start += begin_size;
    size = end_size;
    // begin, end remain unchanged
      } else if (begin - part_start <= begin_size) {
    // 56|#|1234 -> 123 56|#|4
    size_t size_moved = begin - part_start;
    assert(size_moved >= end_size); // else the next step would be more efficient
    if (show_steps) {
      printf("Case 5\n");
    }
    swap_parts(A, N, part_start, begin, end_size, show_steps);
    move_part(A, N, end, begin + end_size, begin - end, show_steps);
    assert(end_size + (begin - end) == size_moved);
    size -= size_moved;
    part_start = begin;
    begin += size_moved;
    end += size_moved;
      } else if (end_size <= begin_size) {
    // 45|##|123 -> 123 #|45|# 
    if (show_steps) {
      printf("Case 6\n");
    }
    swap_parts(A, N, part_start, begin, end_size, show_steps);
    move_part(A, N, end, begin + end_size, begin_size - end_size, show_steps);
    part_start += begin_size;
    size = end_size;
    end = begin + end_size;
    // begin remains unchanged
      } else {
    // No case applies, this should never happen
    assert(0);
      }
    }
  }
}


int main()
{
  int N = 20;
  int A[20];
  size_t size = 17;
  size_t begin = 15;
  size_t i;
  for (i = 0; i < size; ++i) {
    A[(begin + i) % N] = i;
  }
  move_to_beginning(A, N, begin, size, 0);
  for (i = 0; i < size; ++i) {
    printf("%d ", A[i]);
  }
  printf("\n");
  return 0;
}

情况 1:源与目标最多在单个连续区域中重叠,该区域小于整个数组

R.的第一个答案中给出了这种情况的详细解释。我在这里没有什么可补充的。

情况 2:要么源与目标在两个连续区域重叠,要么我们旋转整个数组

最简单的方法始终是旋转整个数组。这也会从目标范围中移动一些不需要的元素,但因为在这种情况下K > N/2,这不会使操作次数超过所需的两倍。

要旋转数组,请使用循环引导算法:取出数组的第一个元素(A[0])并将其复制到目标位置;该位置之前的内容再次移动到正确的位置;继续,直到某个元素移动到起始位置。

继续对下一个起始位置应用循环引导算法:A[1]、A[2]、...、A[GCD(N,d) - 1],其中d是源和目的地之间的距离。

After GCD(N,d)步骤,所有元素都处于适当的位置。这是有效的,因为:

  1. 位置 0, 1, ..., GCD(N,d) - 1 属于不同的循环 - 因为所有这些数字都是不同的(模GCD(N,d)).
  2. 每个周期都有长度N / GCD(N,d)- 因为d / GCD(N,d) and N / GCD(N,d)都是相对质数的。

这个算法很简单,它只移动每个元素一次。它可以是线程安全的(如果我们跳过写入步骤,除非在目标范围内)。其他与多线程相关的优点是每个元素可能只有两个值 - “移动”之前的值和“移动”之后的值(不可能有临时的中间值)。

但它并不总是具有最佳性能。如果element_size * GCD(N,d)与缓存行大小相当,我们可能会采取所有GCD(N,d)起始位置并一起处理它们。如果这个值太大,我们可以将起始位置分成几个连续的段,以将空间需求降低到 O(1)。

问题是当element_size * GCD(N,d)远小于高速缓存行大小。在这种情况下,我们会出现大量缓存未命中并且性能下降。 gusbro 的想法是暂时用一些“交换”区域(大小为d)针对这种情况提出了更有效的算法。如果我们使用适合缓存的“交换”区域,并使用 memcpy 复制非重叠区域,可能会得到更多优化。


又一种算法。它不会覆盖不在目标范围内的元素。而且它是缓存友好的。唯一的缺点是:它将每个元素恰好移动两次。

这个想法是向相反方向移动两个指针并交换指向的元素。重叠区域没有问题,因为重叠区域只是颠倒过来。第一次通过该算法后,我们将所有源元素移动到目标范围,但顺序相反。因此第二遍应该反转目标范围:

for (d = dst_start, s = src_end - 1;
     d != dst_end;
     d = (d + 1) % N, s = (s + N - 1) % N)
  swap(s, d);

for (d = dst_start, s = dst_end - 1;
     d != dst_end;
     d = (d + 1) % N, s = (s + N - 1) % N)
  swap(s, d);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C 将内存部分移动到位 的相关文章

  • 在 HKCR 中创建新密钥有效,但不起作用

    我有以下代码 它返回 成功 但使用两种不同的工具使用搜索字符串 3BDAAC43 E734 11D5 93AF 00105A990292 搜索注册表不会产生任何结果 RegistryKey RK Registry ClassesRoot C
  • 转换 const void*

    我有一个函数返回一个const void 我想用它的信息作为char 我可以将它投射为 C 风格的罚款 char variable但是当我尝试使用reinterpret cast like reinterpret cast
  • 按扩展名过滤搜索文件返回太多结果

    我正在开发一个 C 控制台应用程序 它必须管理 Windows 操作系统上的文件 我需要获取具有特定扩展名的文件名 列表 我找到了很多解决方案 最建议的是以下一种 HANDLE hFind WIN32 FIND DATA data hFin
  • extern 声明和函数定义都在同一文件中

    我只是浏览了一下gcc源文件 在gcc c 我发现了类似的东西 extern int main int char int main int argc char argv 现在我的疑问是extern是告诉编译器特定的函数不在这个文件中 但可以
  • 强制初始化模板类的静态数据成员

    关于模板类的静态数据成员未初始化存在一些问题 不幸的是 这些都没有能够帮助我解决我的具体问题的答案 我有一个模板类 它有一个静态数据成员 必须为特定类型显式实例化 即必须专门化 如果不是这种情况 使用不同的模板函数应该会导致链接器错误 这是
  • cpp.react库的C++源代码中奇怪的“->* []”表达式

    这是我在文档中找到的 C 片段cpp react 库 https github com schlangster cpp react implicit parallelism auto in D MakeVar 0 auto op1 in g
  • 如何在 C# Designer.cs 代码中使用常量字符串?

    如何在 designer cs 文件中引用常量字符串 一个直接的答案是在我的 cs 文件中创建一个私有字符串变量 然后编辑 Designer cs 文件以使用此变量 而不是对字符串进行硬编码 但设计者不喜欢这样抛出错误 我明白为什么这行不通
  • 如何使用 Regex.Replace 从字符串中删除数字?

    我需要使用Regex Replace从字符串中删除所有数字和符号 输入示例 123 abcd33输出示例 abcd 请尝试以下操作 var output Regex Replace input d string Empty The d标识符
  • 在 C# 中检查 PowerShell 执行策略的最佳方法是什么?

    当你跑步时Get ExecutionPolicy在 PowerShell 中 它得到有效的执行政策 https learn microsoft com en us powershell module microsoft powershell
  • 从网页运行 ClickOnce 应用程序,无需用户操作

    我们有一个基于 Java 的 Web 应用程序以及用 C 编写的相同应用程序 如果 java 检查器发现客户端计算机上没有安装 Java 则应该运行该应用程序 这个想法是运行 C 单击一次 http en wikipedia org wik
  • 已发布的 .Net Core 应用程序警告安装 .Net Core,但它已安装

    我制作了一个 WPF 和控制台应用程序 供某人在我无法访问的私人服务器上使用 我使用 Visual Studio 2019 的内置 发布向导 来创建依赖于框架的单文件应用程序 当该人打开 WPF 应用程序时 他们会看到标准警告 他们单击 是
  • 模板外部链接?谁能解释一下吗?

    模板名称具有链接 3 5 非成员函数模板可以有内部链接 任何其他模板名称应具有外部链接 从具有内部链接的模板生成的实体与在其他翻译单元中生成的所有实体不同 我知道使用关键字的外部链接 extern C EX extern C templat
  • 在 C# 中为父窗体中的子窗体控件添加事件处理程序

    我有两种形式 一种是带有按钮和文本框的父表单 单击该按钮时 将打开一个对话框 该子窗体又包含一个文本框和一个按钮 现在我想要的是 每当子表单文本框中的文本更改时 父表单文本框中的文本会自动更改 为了获得这个 我所做的是 Form3 f3 n
  • 如何在 C# 中创建异步方法?

    我读过的每一篇博客文章都会告诉您如何在 C 中使用异步方法 但由于某些奇怪的原因 从未解释如何构建您自己的异步方法来使用 所以我现在有这段代码使用我的方法 private async void button1 Click object se
  • Visual Studio 2015 - Web 项目上缺少共享项目参考选项卡

    我从 MSDN 订阅升级到 Visual Studio 2015 因为我非常兴奋地阅读有关共享项目的信息 当我们想要做的只是重用代码时 不再需要在依赖项中管理 21382 个 nuget 包 所以我构建了一个测试共享项目 其中包含一些代码
  • 了解 Lambda 表达式和委托 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我已经尝试解决这个问题很长一段时间了 阅读在线博客和文章 但到目前为止还没有成功 什么是代表 什么是 Lambda 表达式 两者的优点
  • 没有“对 *this”功能的右值引用的解决方法

    我有一个围绕可移动对象的代理容器类 并希望代理能够隐式生成对底层对象的右值引用 但仅当代理本身被移动时 我相信我将能够按照提案 n2439 实施此行为 将移动语义扩展到 this http www open std org jtc1 sc2
  • 如何在 sql azure 上运行 aspnet_regsql? [复制]

    这个问题在这里已经有答案了 可能的重复 将 ASP NET 成员资格数据库迁移到 SQL Azure https stackoverflow com questions 10140774 migrating asp net membersh
  • 是否允许全局静态标识符以单个 _ 开头?

    换句话说 可能static 文件范围 全局变量恰好以一个下划线开头 而不会产生与 C 实现发生名称冲突的可能性 https www gnu org software libc manual html node Reserved Names
  • MySqlConnectionStringBuilder - 使用证书连接

    我正在尝试连接到 Google Cloud Sql 这是一个 MySql 解决方案 我能够使用 MySql Workbench 进行连接 我如何使用 C 连接MySqlConnectionStringBuilder 我找不到提供这三个证书的

随机推荐

  • PHP,在类属性上调用静态方法

    我希望将对象存储为类的属性 然后我希望能够通过直接引用该属性来调用该类的静态方法 考虑以下 class myModel public static function all return 1 class myClass public mod
  • 无法在 Visual Studio 2022 上热重加载

    我将我的 Web 应用程序从 Visual Studio 2019 移至 2022 预览版 7 但我无法热重载 即使是很小的更改 例如更改 if a b to if a b 并且需要停止调试器 并且我不确定 COMPLUS ForceENC
  • 像音乐应用程序一样自定义 UISlider

    我构建了一个自定义滑块来显示音乐曲目播放的进度并允许在曲目内进行擦洗 两者都运行良好 但一旦停止拖动并且重新定位滑块 就会出现轻微的滞后 和跳跃的运动 Apple Music 应用程序滑块是无缝的 scrubberSlider Scrubb
  • 多维数组到 MVC 控制器

    我有以下控制器方法 public ActionResult Export string data string workbookName ExcelWorkbook workbook new ExcelWorkbook workbook A
  • 如果使用 Match_Constraints,嵌套约束布局不会显示

    我正在尝试在 Android 中创建一个嵌套的 ConstraintLayout 目标是在约束布局内左侧有一个图像 右侧有另一个约束布局 如下图所示 It correctly shows on the preview but inside
  • 在 Internet Explorer 中启用 SOCKS 4a/5

    出于匿名目的 我们希望使用不断变化的代理服务器 在搜索过程中 我们偶然发现了 TOR 项目 它非常适合正常浏览 但是我们还需要软件的代理 遗憾的是 这个第三方软件使用互联网浏览器作为基础 因此我们无法使用推荐的浏览器 更糟糕的是 IE 的代
  • bashrc if:表达式语法错误

    我编写了以下 bashrc bashrc Source global definitions if f etc bashrc then etc bashrc fi User specific aliases and functions fu
  • 更新了 SDK 版本,出现 ClassNotFoundException: android.support.v4.view.ViewPager

    当我在处理 Android 项目时 我发现 Logcat 很烦人 没有将滚动条保持在给定点 并了解到更新 SDK 版本会添加一个暂停按钮来解决此问题 我更新到 SDK 版本 17 现在遇到了一些以前没有的奇怪问题 我删除并添加了 andro
  • VB.Net:测试多个值是否相等?

    如何测试一行中多个值的相等性 基本上我想做 if val1 val2 val3 valN 但在 VB Net 中 If val1 valN AndAlso val2 valN AndAlso Then End If 当测试多个值时 这可能会
  • 如何在SD卡上创建私人文件夹

    我的应用程序用于安全目的 因此 从我的应用程序用户捕获的照片中 所有照片都存储在一个文件夹中 该文件夹不应从任何其他应用程序访问 并且当设备连接到计算机系统时不应授予访问权限 如果用户想查看这些图像 他应该只能从我的应用程序访问 根据这个g
  • 设置 UIView 的 self 背景颜色

    我正在尝试从自定义视图类的 m 内部执行此操作not从 XIB 加载 而是以编程方式加载 id initWithFrame CGRect frame self super initWithFrame frame if self Initia
  • 未找到 ${env.JAVA_HOME} - Ant

    在我的 build xml 文件中 我有以下几行
  • 在 MySQL 中正确实现超类型子类型

    下面是一个数据库图表 我试图在其中确定适当的设计 这里有一些注意事项 员工 经理与客户相关联 The partyid是一种在全球范围内代表一个人的方式 客户 员工 经理 需要一直向下传播吗 它应该是所有表中的主键还是仅代表个人的表中的主键
  • 无法解析方法 getMap()

    我试图让地图片段在我的应用程序中工作 但在尝试获取 GoogleMap 对象时仍然出现错误 FragmentWithMap java import android Manifest import android app Activity i
  • string::size_type 而不是 int

    const std string size type cols greeting size pad 2 2 Why string size type int应该可以工作 它包含数字 空头也能容纳数字 与签名字符一样 但这些类型都不能保证足够
  • 当委托构造函数抛出异常时,内存是否会自动回收?

    从此 当委托构造函数抛出异常时 析构函数是否被调用 https stackoverflow com q 17657761 14065 class X public X X int X throw std exception X double
  • 将 GitHub 文件(和更新)获取到 Ubuntu Web 服务器上

    我正在设置一个多用户 多服务器环境 所有开发人员都将使用 Git 并从 GitHub 等克隆各种存储库 在我控制的一个帐户中 现在 我如何将文件从 GitHub 获取到服务器 大约 5 个 首先 我正在考虑某种自动化方式将更新从 GutHu
  • SSRS 报告查看器 + ASP.NET 凭据 401 异常

    我在 SQL2005 报告服务器上保存了一份报告 我想返回该报告的渲染 PDF 我在使用本地 rdlc 文件时发现了这一点 我已经在博客上介绍过它 http www jarrettmeyer com 2009 09 reports in a
  • Jest - “child_process”包中的模拟函数

    我正在编写单元测试 并模拟包 child process 中的 exec 方法 mocks child process js const child process jest genMockFromModule child process
  • C 将内存部分移动到位

    我正在实现几个数据结构 我想要使用的一个原语如下 我有一个内存块 A N 它的长度可变 但我在示例中使用 100 在这个块内 有一个较小的部分C 长度为 K 假设为 30 我想在不使用任何额外内存的情况下移动它 额外的困难是 A 换行 即C