[{"data":1,"prerenderedAt":329},["ShallowReactive",2],{"article-8":3},{"id":4,"title":5,"body":6,"create":317,"description":12,"extension":318,"labels":319,"locked":321,"meta":322,"navigation":323,"path":324,"seo":325,"stem":326,"update":327,"__hash__":328},"articles/article/8.md","生成排列问题",{"type":7,"value":8,"toc":311},"minimark",[9,13,17,20,23,34,38,281,287,290,293,305],[10,11,12],"p",{},"对于 1-5 生成它的全排列，这里只列出三个方法：自底向上生成排列（插入）、Johnson-Trotter\n算法、字典序算法。但是本人没有考虑到边界问题，所以可能处理边界情况的时候会报错。",[14,15,16],"h2",{"id":16},"自底向上生成排列",[10,18,19],{},"自底向上生成排列，说白了就是在一个序列的空隙中插入待排列数据，如果现在只有一个元素，它的全排列就只有一个 {1}。那么它就会有两个位置可以插入\n2，插入之后就有两个排列，也就是 {2, 1}、{1, 2}，这两个排列都有三个位置可以插入 3。3 个元素的全排列就是 {3, 2, 1}、{2, 3,\n1}、{2, 1, 3}、{3, 1, 2}、{1, 3, 2}、{1, 2, 3}。",[10,21,22],{},"我们可以借助递归来实现这一算法：",[24,25,31],"pre",{"className":26,"code":28,"language":29,"meta":30},[27],"language-c++","#include \u003Cvector>\n#include \u003Ciostream>\nusing namespace std;\nvector\u003Cvector\u003Cint>> result;\nvector\u003Cint>::iterator it;\n\nvoid AllSort(int n) {\n  // 存储待插入元素的排列 \n  vector\u003Cint> curr; \n  for (int i = 1; i \u003C n; i++) {\n    // 暂时存储本轮的全排列\n    vector\u003Cvector\u003Cint>> temp;\n    // 记录插入的位置\n    int j = 0;\n    // 遍历上一轮中的全排列\n    for (int k = 0; k \u003C result.size(); k++) {\n      for (int j = 0; j \u003C i + 1; j++) {\n        // 取出上一轮的全排列\n        curr = result[k];\n        // 计算插入位置\n        it = curr.begin() + j;\n        // 插入元素\n        curr.insert(it, i + 1);\n        // 暂存全排列\n        temp.push_back(curr);\n      }\n    }\n    result = temp;\n  }\n}\n\nint main() {\n  // 初始化，先插入仅有一个元素的排列\n  result.push_back({1});\n  AllSort(4);\n  // 输出\n  for (int n = 0; n \u003C result.size(); n++) {\n    cout \u003C\u003C '{';\n    for (int m = 0; m \u003C result[n].size(); m++) {\n      cout \u003C\u003C result[n][m];\n      if (m != result[n].size() - 1)\n        cout \u003C\u003C ',';\n    }\n    cout \u003C\u003C '}' \u003C\u003C endl;\n  }\n  return 0;\n}\n","c++","",[32,33,28],"code",{"__ignoreMap":30},[14,35,37],{"id":36},"johnson-trotter-算法","Johnson-Trotter 算法",[10,39,40,41,280],{},"JT 算法对于 1-5 数字全排列是这么做的：\n",[42,43,46,97],"span",{"className":44},[45],"katex",[42,47,50],{"className":48},[49],"katex-mathml",[51,52,54],"math",{"xmlns":53},"http://www.w3.org/1998/Math/MathML",[55,56,57,92],"semantics",{},[58,59,60,74,83],"mrow",{},[61,62,63,70],"mover",{},[64,65,66],"mo",{},[67,68,69],"mn",{},"1",[64,71,73],{"lspace":72,"rspace":72},"0em","←",[61,75,76,81],{},[64,77,78],{},[67,79,80],{},"2",[64,82,73],{"lspace":72,"rspace":72},[61,84,85,90],{},[64,86,87],{},[67,88,89],{},"3",[64,91,73],{"lspace":72,"rspace":72},[93,94,96],"annotation",{"encoding":95},"application/x-tex","\\mathop{1}\\limits^{\\leftarrow}\\mathop{2}\\limits^{\\leftarrow}\\mathop{3}\\limits^{\\leftarrow}",[42,98,102],{"className":99,"ariaHidden":101},[100],"katex-html","true",[42,103,106,111,178,183,230,233],{"className":104},[105],"base",[42,107],{"className":108,"style":110},[109],"strut","height:1.2012em;vertical-align:-0.0722em;",[42,112,116],{"className":113},[114,115],"mop","op-limits",[42,117,121,169],{"className":118},[119,120],"vlist-t","vlist-t2",[42,122,125,164],{"className":123},[124],"vlist-r",[42,126,130,143],{"className":127,"style":129},[128],"vlist","height:1.129em;",[42,131,133,138],{"style":132},"top:-2.9278em;",[42,134],{"className":135,"style":137},[136],"pstrut","height:3em;",[42,139,140],{},[42,141,69],{"className":142},[114],[42,144,146,149],{"style":145},"top:-3.7722em;margin-left:0em;",[42,147],{"className":148,"style":137},[136],[42,150,156],{"className":151},[152,153,154,155],"sizing","reset-size6","size3","mtight",[42,157,160],{"className":158},[159,155],"mord",[42,161,73],{"className":162},[163,155],"mrel",[42,165,168],{"className":166},[167],"vlist-s","​",[42,170,172],{"className":171},[124],[42,173,176],{"className":174,"style":175},[128],"height:0.0722em;",[42,177],{},[42,179],{"className":180,"style":182},[181],"mspace","margin-right:0.1667em;",[42,184,186],{"className":185},[114,115],[42,187,189,222],{"className":188},[119,120],[42,190,192,219],{"className":191},[124],[42,193,195,205],{"className":194,"style":129},[128],[42,196,197,200],{"style":132},[42,198],{"className":199,"style":137},[136],[42,201,202],{},[42,203,80],{"className":204},[114],[42,206,207,210],{"style":145},[42,208],{"className":209,"style":137},[136],[42,211,213],{"className":212},[152,153,154,155],[42,214,216],{"className":215},[159,155],[42,217,73],{"className":218},[163,155],[42,220,168],{"className":221},[167],[42,223,225],{"className":224},[124],[42,226,228],{"className":227,"style":175},[128],[42,229],{},[42,231],{"className":232,"style":182},[181],[42,234,236],{"className":235},[114,115],[42,237,239,272],{"className":238},[119,120],[42,240,242,269],{"className":241},[124],[42,243,245,255],{"className":244,"style":129},[128],[42,246,247,250],{"style":132},[42,248],{"className":249,"style":137},[136],[42,251,252],{},[42,253,89],{"className":254},[114],[42,256,257,260],{"style":145},[42,258],{"className":259,"style":137},[136],[42,261,263],{"className":262},[152,153,154,155],[42,264,266],{"className":265},[159,155],[42,267,73],{"className":268},[163,155],[42,270,168],{"className":271},[167],[42,273,275],{"className":274},[124],[42,276,278],{"className":277,"style":175},[128],[42,279],{},"\n这是初始状态，如果说一个元素指向的元素比他小，那么就说这个元素可以移动，找到其中最大的可移动元素 A，移动，直到 A 不能移动再查找序列中比\nA 大的元素，反转方向，再次循环。",[24,282,285],{"className":283,"code":284,"language":29,"meta":30},[27],"#include \u003Ciostream>\nusing namespace std;\n// 交换元素\nvoid swap(int& a, int& b) {\n  int temp = a;\n  a = b;\n  b = temp;\n}\n// 判断元素是否能够移动\nbool IsMovable(int arr[], int mark[], int index, int n) {\n  int next = index + mark[index];\n  // 下标小于 0 或者大于 n 都不能移动\n  if (next \u003C 0) return false;\n  if (next > n - 1) return false;\n  // 如果本元素大于下一个元素，则可以移动\n  return arr[index] > arr[next];\n}\n// 反转标记\nvoid RevertMark(int &mark) {\n  if (mark == -1) mark = 1;\n  else mark = -1;\n}\n// 打印数组\nvoid PrintArr(int arr[], int n) {\n  cout \u003C\u003C '{';\n  for (int i = 0; i \u003C n; i++) {\n    cout \u003C\u003C arr[i];\n    if (i != n - 1) cout \u003C\u003C ',';\n  }\n  cout \u003C\u003C '}' \u003C\u003C endl;\n}\n\nvoid JT(int n) {\n  // 分配待排序序列空间\n  int* arr = (int*)malloc(sizeof(int) * n);\n  // 分配标记空间\n  int* mark = (int*)malloc(sizeof(int) * n);\n  // 标记序列中是否还有能够移动的标记\n  int count = n;\n  // 初始化待排序序列和标记数组\n  for (int i = 0; i \u003C n; i++) {\n    arr[i] = i + 1;\n    mark[i] = -1;\n  }\n  // 最初的状态也是一个排列\n  PrintArr(arr, n);\n  while(count>0){\n    int k = -1, index = -1;\n    // 计算序列中所有可以移动的元素\n    for (int i = 0; i \u003C n; i++) {\n      if (IsMovable(arr, mark, i, n) && arr[i] > k) {\n        k = arr[i], index = i;\n      }\n    }\n    // 计算元素移动位置\n    int next = index + mark[index];\n    // 移动元素\n    while (IsMovable(arr, mark, index, n)) {\n      swap(arr[index], arr[next]);\n      swap(mark[index], mark[next]);\n      index = next;\n      next = index + mark[index];\n      count--;\n      PrintArr(arr, n);\n      //PrintArr(mark, n);\n    }\n    // 反转所有比这次移动元素大的元素方向并判断是否可以移动\n    for (int i = 0; i \u003C n; i++) {\n      if (arr[i] > k) RevertMark(mark[i]);\n      if (IsMovable(arr, mark, i, n)) count++;\n    }\n  }\n\n}\nint main() {\n  JT(3);\n  return 0;\n}\n",[32,286,284],{"__ignoreMap":30},[14,288,289],{"id":289},"字典序算法",[10,291,292],{},"先从右往左，找出第一个比右边元素小的元素 A ，再从右往左找到第一个比 A 大的元素 B，交换 A 和 B 之后，A 的下标后移，接着反转从\nA 到序列最右边的所有元素，如果有比右边的元素小的元素，则继续下次循环。如：",[10,294,295,296,300,301,304],{},"1234 先找到 3 ，再从右到左找到第一个比 3 大的 4，交换 3 4 得 ",[297,298,299],"strong",{},"1243","；接着找到 A:2，B:3，交换：",[297,302,303],{},"1342","，反转 1324；接着 A:2，B:\n4，交换：1342，以此类推，可以得到 1234 这 4 个元素的全排列。",[24,306,309],{"className":307,"code":308,"language":29,"meta":30},[27],"#include \u003Ciostream>\nusing namespace std;\n// 交换元素\nvoid swap(int& a, int& b) {\n  int temp = a;\n  a = b;\n  b = temp;\n}\n// 判断是否可以继续排列，如果有比右边元素小的元素，那么可以继续排列\nbool ContinueSort(int arr[], int n) {\n  for (int i = 0; i \u003C n-1; i++) {\n    if (arr[i] \u003C arr[i + 1]) return true;\n  }\n  return false;\n}\n// 打印数组\nvoid PrintArr(int arr[], int n) {\n  cout \u003C\u003C '{';\n  for (int i = 0; i \u003C n; i++) {\n    cout \u003C\u003C arr[i];\n    if (i != n - 1) cout \u003C\u003C ',';\n  }\n  cout \u003C\u003C '}' \u003C\u003C endl;\n}\n\nvoid ZiDianSort(int arr[], int n) {\n  int a = 0, b = 0;\n  while(ContinueSort(arr,n)){\n    for (int i = n - 1; i > 0; i--) {\n      // 从右往左找到第一个比右边小的元素\n      if (arr[i] > arr[i - 1]) {\n        // 记录该元素的下标 a\n        a = i - 1;\n        break;\n      }\n    }\n    // 再从右往左找到第一个比下标 a 元素大的元素\n    for (int j = n - 1; j > 0; j--) {\n      if (arr[j] > arr[a]) {\n        // 记录下标为 b\n        b = j;\n        break;\n      }\n    }\n    // 交换两个元素\n    swap(arr[a], arr[b]);\n    // a 的下标后移\n    a++;\n    b = n-1;\n    // 反转从 a 到 序列最右边的元素\n    while (a \u003C b) {\n      swap(arr[a++], arr[b--]);\n    }\n    PrintArr(arr, n);\n  }\n}\n\nint main() {\n  int n = 4;\n  int* arr = (int*)malloc(sizeof(int) * n);\n  for (int i = 0; i \u003C n; i++) {\n    arr[i] = i + 1;\n  }\n  PrintArr(arr, n);\n  ZiDianSort(arr,n);\n  return 0;\n}\n",[32,310,308],{"__ignoreMap":30},{"title":30,"searchDepth":312,"depth":312,"links":313},2,[314,315,316],{"id":16,"depth":312,"text":16},{"id":36,"depth":312,"text":37},{"id":289,"depth":312,"text":289},"2023-11-16T09:13:03.000Z","md",[320],"算法",false,{},true,"/article/8",{"title":5,"description":12},"article/8","2023-11-17T14:47:00.000Z","a2_XFISo-yLELh2qi4--QsRUeyliTGNAUeEMkTcEYJc",1755235549203]