搜尋演算法“一二”基於VSCode平臺C#語言
阿新 • • 發佈:2020-08-14
近日有個門店的查詢需求,但是有集團下的各個門店,像AD系統的森林-》樹-》樹杈-》葉子節點。
相關演算法擴充套件思路,以視參考:
using System; namespace Arithmetic_c_ { class Program { static void Main(string[] args) { //Console.WriteLine("Hello World!"); //InitBlockSearch(); int[] a = new intView Code[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; int checkInt=12; int result= ProgramArithmetic.FbSearch(a,checkInt); } //813 //演算法1 public struct IndexBlock { public int max; public int start;public int end; }; const int BLOCK_COUNT = 3; private static void InitBlockSearch() { int j = -1; int k = 0; int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; IndexBlock[] indexBlock = new IndexBlock[BLOCK_COUNT];for (int i = 0; i < BLOCK_COUNT; i++) { indexBlock[i].start = j + 1; //確定每個塊範圍的起始值 j = j + 1; indexBlock[i].end = j + 4; //確定每個塊範圍的結束值 j = j + 4; indexBlock[i].max = a[j]; //確定每個塊範圍中元素的最大值 } k = BlockSearch(12, a, indexBlock); if (k >= 0) { Console.WriteLine("查詢成功!你要查詢的數在陣列中的索引是:{0}\n", k); } else { Console.WriteLine("查詢失敗!你要查詢的數不在陣列中。\n"); } } /// <summary> /// 分塊查詢 /// 分塊查詢要求把一個數據分為若干塊,每一塊裡面的元素可以是無序的,但是塊與塊之間的元素需要是有序的。 /// (對於一個非遞減的數列來說,第i塊中的每個元素一定比第i-1塊中的任意元素大) /// </summary> private static int BlockSearch(int x, int[] a, IndexBlock[] indexBlock) { int i = 0; int j; while (i < BLOCK_COUNT && x > indexBlock[i].max) { //確定在哪個塊中 i++; } if (i >= BLOCK_COUNT) { //大於分的塊數,則返回-1,找不到該數 return -1; } //j等於塊範圍的起始值 j = indexBlock[i].start; while (j <= indexBlock[i].end && a[j] != x) { //在確定的塊內進行查詢 j++; } if (j > indexBlock[i].end) { //如果大於塊範圍的結束值,則說明沒有要查詢的數,j置為-1 j = -1; } return j; } //演算法1 //演算法2 //演算法2 //813 } }
using System; namespace Arithmetic_c_ { class ProgramArithmetic { /// <summary> /// 斐波那契查詢就是在二分查詢的基礎上根據斐波那契數列進行分割的。 /// 在斐波那契數列找一個等於略大於查詢表中元素個數的數F[n], /// 將原查詢表擴充套件為長度為F[n](如果要補充元素,則補充重複最後一個元素,直到滿足F[n]個元素), /// 完成後進行斐波那契分割,即F[n]個元素分割為前半部分F[n-1]個元素,後半部分F[n-2]個元素, /// 那麼前半段元素個數和整個有序表長度的比值就接近黃金比值0.618, /// 找出要查詢的元素在那一部分並遞迴,直到找到。 /// middle = low + fb[k - 1] - 1 /// </summary> public static int FbSearch(int[] arr, int value) { if (arr == null || arr.Length == 0) { return -1; } int length = arr.Length; // 建立一個長度為20的斐波數列 int[] fb = MakeFbArray(20); int k = 0; while (length > fb[k] - 1) { // 找出陣列的長度在斐波數列(減1)中的位置,將決定如何拆分 k++; } // 滿足黃金比例分割 if (length == fb[k - 1]) { return FindFbSearch(arr, fb, --k, value, length); } else { // 構造一個長度為fb[k] - 1的新數列 int[] temp = new int[fb[k] - 1]; // 把原陣列拷貝到新的陣列中 arr.CopyTo(temp, 0); int tempLen = temp.Length; for (int i = length; i < tempLen; i++) { // 從原陣列長度的索引開始,用最大的值補齊新數列 temp[i] = arr[length - 1]; } return FindFbSearch(temp, fb, k, value, length); } } private static int FindFbSearch(int[] arr, int[] fb, int k, int value, int length) { int low = 0; int hight = length - 1; while (low <= hight) { // 黃金比例分割點 int middle = low + fb[k - 1] - 1; if (arr[middle] > value) { hight = middle - 1; // 全部元素 = 前半部分 + 後半部分 // 根據斐波那契數列進行分割,F(n)=F(n-1)+F(n-2) // 因為前半部分有F(n-1)個元素,F(n-1)=F(n-2)+F(n-3), // 為了得到前半部分的黃金分割點n-2, // int middle = low + fb[k - 1] - 1; k已經減1了 // 所以k = k - 1 k = k - 1; } else if (arr[middle] < value) { low = middle + 1; // 全部元素 = 前半部分 + 後半部分 // 根據斐波那契數列進行分割,F(n)=F(n-1)+F(n-2) // 因為後半部分有F(n-2)個元素,F(n-2)=F(n-3)+F(n-4), // 為了得到後半部分的黃金分割點n-3, // int middle = low + fb[k - 1] - 1; k已經減1了 // 所以k = k - 2 k = k - 2; } else { if (middle <= hight) { return middle;// 若相等則說明mid即為查詢到的位置 } else { return hight;// middle的值已經大於hight,進入擴充套件陣列的填充部分,即原陣列最後一個數就是要查詢的數 } } } return -1; } public static int[] MakeFbArray(int length) { int[] array = null; if (length > 2) { array = new int[length]; array[0] = 1; array[1] = 1; for (int i = 2; i < length; i++) { array[i] = array[i - 1] + array[i - 2]; } } return array; } } }View Code
using System; using System.Collections.Generic; namespace Arithmetic_c_ { /// 紅黑樹定義: /// 性質1.節點是紅色或黑色 /// 性質2.根是黑色 /// 性質3.所有葉子都是黑色(葉子是NIL節點) /// 性質4.如果一個節點是紅的,則它的兩個子節點都是黑的(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點) /// 性質5.從任一節點到其葉子的所有路徑都包含相同數目的黑色節點。 public class RedBlackTree<T> { //根節點 private RedBlackTreeNode<T> mRoot; //比較器 private Comparer<T> mComparer; private const bool RED = true; private const bool BLACK = false; public RedBlackTree() { mRoot = null; mComparer = Comparer<T>.Default; } public bool Contains(T value) { RedBlackTreeNode<T> node; return Contain(value, out node); } public bool Contain(T value, out RedBlackTreeNode<T> newNode) { if (value == null) { throw new ArgumentNullException(); } newNode = null; RedBlackTreeNode<T> node = mRoot; while (node != null) { int comparer = mComparer.Compare(value, node.Data); if (comparer > 0) { node = node.RightChild; } else if (comparer < 0) { node = node.LeftChild; } else { newNode = node; return true; } } return false; } public void Add(T value) { if (mRoot == null) { // 根節點是黑色的 mRoot = new RedBlackTreeNode<T>(value, BLACK); } else { // 新插入節點是紅色的 Insert1(new RedBlackTreeNode<T>(value, RED), value); } } private void Insert1(RedBlackTreeNode<T> newNode, T value) { //遍歷找到插入位置 RedBlackTreeNode<T> node = mRoot; //插入節點的父節點 RedBlackTreeNode<T> parent = null; while (node != null) { parent = node; int comparer = mComparer.Compare(value, node.Data); if (comparer > 0) { node = node.RightChild; } else if (comparer < 0) { node = node.LeftChild; } else { node.Data = value; return; } } //找到插入位置,設定新插入節點的父節點為current newNode.Parent = parent; //比較插入節點的值跟插入位置的值的大小, 插入新節點 int comparer1 = mComparer.Compare(value, parent.Data); if (comparer1 > 0) { parent.RightChild = newNode; } else if (comparer1 < 0) { parent.LeftChild = newNode; } //將它重新修整為一顆紅黑樹 InsertFixUp(newNode); } private void InsertFixUp(RedBlackTreeNode<T> newNode) { RedBlackTreeNode<T> parent = newNode.Parent; //插入節點的父節點 RedBlackTreeNode<T> gParent = null; //插入節點的祖父節點 //父節點的顏色是紅色,並且不為空 while (IsRed(parent) && parent != null) { //獲取祖父節點,這裡不用判空, //因為如果祖父節點為空,parent就是根節點,根節點是黑色,不會再次進入迴圈 gParent = parent.Parent; //若父節點是祖父節點的左子節點 if (parent == gParent.LeftChild) { RedBlackTreeNode<T> uncle = gParent.RightChild; //獲得叔叔節點 //case1: 叔叔節點也是紅色 if (uncle != null && IsRed(uncle)) { //把父節點和叔叔節點塗黑,祖父節點塗紅 parent.Color = BLACK; uncle.Color = BLACK; gParent.Color = RED; //把祖父節點作為插入節點,向上繼續遍歷 newNode = gParent; parent = newNode.Parent; continue; //繼續while,重新判斷 } //case2: 叔叔節點是黑色,且當前節點是右子節點 if (newNode == parent.RightChild) { //從父節點處左旋 //當這種情況時,只能左旋,因為父親節點和祖父節點變色,無論左旋還是右旋,都會違背紅黑樹的基本性質 RotateLeft(parent); //當左旋後,紅黑樹變成case3的情況,區別就是插入節點是父節點 //所以,將父節點和插入節點調換一下,為下面右旋做準備 RedBlackTreeNode<T> tmp = parent; parent = newNode; newNode = tmp; } //case3: 叔叔節點是黑色,且當前節點是左子節點 // 父親和祖父節點變色,從祖父節點處右旋 parent.Color = BLACK; gParent.Color = RED; RotateRight(gParent); } else { //若父節點是祖父節點的右子節點,與上面的完全相反 RedBlackTreeNode<T> uncle = gParent.LeftChild; //case1: 叔叔節點也是紅色 if (uncle != null & IsRed(uncle)) { //把父節點和叔叔節點塗黑,祖父節點塗紅 parent.Color = BLACK; uncle.Color = BLACK; gParent.Color = RED; //把祖父節點作為插入節點,向上繼續遍歷 newNode = gParent; parent = newNode.Parent; continue;//繼續while,重新判斷 } //case2: 叔叔節點是黑色的,且當前節點是左子節點 if (newNode == parent.LeftChild) { //從父節點處右旋 //當這種情況時,只能右旋,因為父親節點和祖父節點變色,無論左旋還是右旋,都會違背紅黑樹的基本性質 RotateRight(parent); RedBlackTreeNode<T> tmp = parent; parent = newNode; newNode = tmp; } //case3: 叔叔節點是黑色的,且當前節點是右子節點 // 父親和祖父節點變色,從祖父節點處右旋 parent.Color = BLACK; gParent.Color = RED; RotateLeft(gParent); } } //將根節點設定為黑色 mRoot.Color = BLACK; } public bool IsRed(RedBlackTreeNode<T> node) { if (node == null) { return false; } if (node.Color == RED) { return true; } return false; } public bool IsBlack(RedBlackTreeNode<T> node) { if (node == null) { return false; } if (node.Color == BLACK) { return true; } return false; } // 左旋轉,逆時針旋轉 /*************對紅黑樹節點x進行左旋操作 ******************/ /* * 左旋示意圖:對節點x進行左旋 * p p * / / * x y * / \ / \ * lx y x ry * / \ / \ * ly ry lx ly * 左旋做了三件事: * 1. 將y的左子節點賦給x的右子節點,並將x賦給y左子節點的父節點(y左子節點非空時) * 2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點為y(左或右) * 3. 將y的左子節點設為x,將x的父節點設為y */ private void RotateLeft(RedBlackTreeNode<T> x) { //1. 將y的左子節點賦給x的右子節點,並將x賦給y左子節點的父節點(y左子節點非空時) RedBlackTreeNode<T> y = x.RightChild; x.RightChild = y.LeftChild; if (y.LeftChild != null) { y.LeftChild.Parent = x; } //2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點為y(左或右) if (x.Parent != null) { y.Parent = x.Parent; } if (x.Parent == null) { mRoot = y; //如果x的父節點為空,則將y設為父節點 y.Parent = null;//設定y的父節點為空 } else { //如果x是左子節點 if (x == x.Parent.LeftChild) { //則也將y設為左子節點 x.Parent.LeftChild = y; } else { //否則將y設為右子節點 x.Parent.RightChild = y; } } //3. 將y的左子節點設為x,將x的父節點設為y y.LeftChild = x; x.Parent = y; } // 右旋轉,順時針旋轉 /*************對紅黑樹節點y進行右旋操作 ******************/ /* * 左旋示意圖:對節點y進行右旋 * p p * / / * y x * / \ / \ * x ry lx y * / \ / \ * lx rx rx ry * 右旋做了三件事: * 1. 將x的右子節點賦給y的左子節點,並將y賦給x右子節點的父節點(x右子節點非空時) * 2. 將y的父節點p(非空時)賦給x的父節點,同時更新p的子節點為x(左或右) * 3. 將x的右子節點設為y,將y的父節點設為x */ private void RotateRight(RedBlackTreeNode<T> y) { //1.將x的右子節點賦值給y的左子節點,同時將y賦值給x的右子節點的父節點(如果x的右子節點非空) RedBlackTreeNode<T> x = y.LeftChild; y.LeftChild = x.RightChild; if (x.RightChild != null) { x.RightChild.Parent = y; } //2.如果y的父節點非空時,將y的父節點賦值給x的父節點,同時更新p的子節點為x if (y.Parent != null) { x.Parent = y.Parent; } //如果y的父節點為空,則將x設為父節點 if (y.Parent == null) { mRoot = x; x.Parent = null;//設定x的父節點為空 } else { //如果y是右子節點 if (y == y.Parent.RightChild) { //則也將y設為右子節點 y.Parent.RightChild = x; } else { //否則將x設為左子節點 y.Parent.LeftChild = x; } } //3.將x的右子節點設為y,y的父節點設定為x x.RightChild = y; y.Parent = x; } public int Count { get { return CountLeafNode(mRoot); } } private int CountLeafNode(RedBlackTreeNode<T> root) { if (root == null) { return 0; } else { return CountLeafNode(root.LeftChild) + CountLeafNode(root.RightChild) + 1; } } public int Depth { get { return GetHeight(mRoot); } } private int GetHeight(RedBlackTreeNode<T> root) { if (root == null) { return 0; } int leftHight = GetHeight(root.LeftChild); int rightHight = GetHeight(root.RightChild); return leftHight > rightHight ? leftHight + 1 : rightHight + 1; } public T Max { get { RedBlackTreeNode<T> node = mRoot; while (node.RightChild != null) { node = node.RightChild; } return node.Data; } } public T Min { get { if (mRoot != null) { RedBlackTreeNode<T> node = GetMinNode(mRoot); return node.Data; } else { return default(T); } } } public void DelMin() { mRoot = DelMin(mRoot); } private RedBlackTreeNode<T> DelMin(RedBlackTreeNode<T> node) { if (node.LeftChild == null) { return node.RightChild; } node.LeftChild = DelMin(node.LeftChild); return node; } public void Remove(T value) { mRoot = Delete(mRoot, value); } private RedBlackTreeNode<T> Delete(RedBlackTreeNode<T> node, T value) { if (node == null) { Console.WriteLine("沒有找到要刪除的節點: " + value); return null; } int comparer = mComparer.Compare(value, node.Data); if (comparer > 0) { node.RightChild = Delete(node.RightChild, value); } else if (comparer < 0) { node.LeftChild = Delete(node.LeftChild, value); } else { // a.如果刪除節點沒有子節點,直接返回null // b.如果只有一個子節點,返回其子節點代替刪除節點即可 if (node.LeftChild == null) { if (node.RightChild != null) { node.RightChild.Parent = node.Parent; } return node.RightChild; } else if (node.RightChild == null) { if (node.LeftChild != null) { node.LeftChild.Parent = node.Parent; } return node.LeftChild; } else { // c.被刪除的節點“左右子節點都不為空”的情況 RedBlackTreeNode<T> child; RedBlackTreeNode<T> parent; bool color; // 1. 先找到“刪除節點的右子樹中的最小節點”,用它來取代被刪除節點的位置 // 注意:這裡也可以選擇“刪除節點的左子樹中的最大節點”作為被刪除節點的替換節點 RedBlackTreeNode<T> replace = node; replace = GetMinNode(replace.RightChild); // 2. 更新刪除父節點及其子節點 // 要刪除的節點不是根節點 if (node.Parent != null) { // 要刪除的節點是:刪除節點的父節點的左子節點 if (node == node.Parent.LeftChild) { // 把“刪除節點的右子樹中的最小節點”賦值給“刪除節點的父節點的左子節點” node.Parent.LeftChild = replace; } else { // 把“刪除節點的右子樹中的最小節點”賦值給“刪除節點的父節點的右子節點” node.Parent.RightChild = replace; } } else { // 要刪除的節點是根節點 // 如果只有一個根節點,把mRoot賦值為null,這時replace為null // 如果不止一個節點,返回根節點的右子樹中的最小節點 mRoot = replace; } // 記錄被刪除節點的右子樹中的最小節點的右子節點,父親節點及顏色,沒有左子節點 child = replace.RightChild; parent = replace.Parent; color = replace.Color; // 3. 刪除“被刪除節點的右子樹中的最小節點”,同時更新替換節點的左右子節點,父親節點及顏色 // 替換節點 也就是 最小節點 if (parent == node) { // 被刪除節點的右子樹中的最小節點是被刪除節點的子節點 parent = replace; } else { //如果最小節點的右子節點不為空,更新其父節點 if (child != null) { child.Parent = parent; } //更新最小節點的父節點的左子節點,指向最小節點的右子節點 parent.LeftChild = child; //更新替換節點的右子節點 replace.RightChild = node.RightChild; //更新刪除節點的右子節點的父節點 node.RightChild.Parent = replace; } //更新替換節點的左右子節點,父親節點及顏色 replace.Parent = node.Parent; //保持原來位置的顏色 replace.Color = node.Color; replace.LeftChild = node.LeftChild; //更新刪除節點的左子節點的父節點 node.LeftChild.Parent = replace; //紅黑樹平衡修復 //如果刪除的最小節點顏色是黑色,需要重新平衡紅黑樹 //如果刪除的最小節點顏色是紅色,只需要替換刪除節點後,塗黑即可 //上面的保持原來位置的顏色已經處理了這種情況,這裡只需要判斷最小節點是黑色的情況 if (color == BLACK) { //將最小節點的child和parent傳進去 RemoveFixUp(child, parent); } return replace; } } return node; } private void RemoveFixUp(RedBlackTreeNode<T> node, RedBlackTreeNode<T> parent) { RedBlackTreeNode<T> brother; // 被刪除節點的右子樹中的最小節點 不是 被刪除節點的子節點的情況 while ((node == null || IsBlack(node)) && (node != mRoot)) { if (parent.LeftChild == node) { //node是左子節點,下面else與這裡的剛好相反 brother = parent.RightChild; //node的兄弟節點 if (IsRed(brother)) { //case1: node的兄弟節點brother是紅色的 brother.Color = BLACK; parent.Color = RED; RotateLeft(parent); brother = parent.RightChild; } //case2: node的兄弟節點brother是黑色的,且brother的兩個子節點也都是黑色的 //繼續向上遍歷 if ((brother.LeftChild == null || IsBlack(brother.LeftChild)) && (brother.RightChild == null || IsBlack(brother.RightChild))) { //把兄弟節點設定為黑色,平衡紅黑樹 brother.Color = RED; node = parent; parent = node.Parent; } else { //case3: node的兄弟節點brother是黑色的,且brother的左子節點是紅色,右子節點是黑色 if (brother.RightChild == null || IsBlack(brother.RightChild)) { brother.LeftChild.Color = BLACK; brother.Color = RED; RotateRight(brother); brother = parent.RightChild; } //case4: node的兄弟節點brother是黑色的,且brother的右子節點是紅色,左子節點任意顏色 brother.Color = parent.Color; parent.Color = BLACK; brother.RightChild.Color = BLACK; RotateLeft(parent); node = mRoot; break; } } else { //與上面的對稱 brother = parent.LeftChild; if (IsRed(brother)) { // Case 1: node的兄弟brother是紅色的 brother.Color = BLACK; parent.Color = RED; RotateRight(parent); brother = parent.LeftChild; } // Case 2: node的兄弟brother是黑色,且brother的倆個子節點都是黑色的 if ((brother.LeftChild == null || IsBlack(brother.LeftChild)) && (brother.RightChild == null || IsBlack(brother.RightChild))) { //把兄弟節點設定為黑色,平衡紅黑樹 brother.Color = RED; node = parent; parent = node.Parent; } else { // Case 3: node的兄弟brother是黑色的,並且brother的左子節點是紅色,右子節點為黑色。 if (brother.LeftChild == null || IsBlack(brother.LeftChild)) { brother.RightChild.Color = BLACK; brother.Color = RED; RotateLeft(brother); brother = parent.LeftChild; } // Case 4: node的兄弟brother是黑色的;並且brother的左子節點是紅色的,右子節點任意顏色 brother.Color = parent.Color; parent.Color = BLACK; brother.LeftChild.Color = BLACK; RotateRight(parent); node = mRoot; break; } } } //如果刪除的最小節點的右子節點是紅色,只需要替換最小節點後,塗黑即可 if (node != null) { node.Color = BLACK; } } private RedBlackTreeNode<T> GetMinNode(RedBlackTreeNode<T> node) { while (node.LeftChild != null) { node = node.LeftChild; } return node; } // 中序遍歷:首先遍歷其左子樹,然後訪問根結點,最後遍歷其右子樹。 // 遞迴方法實現體內再次呼叫方法本身的本質是多個方法的簡寫,遞迴一定要有出口 public void ShowTree() { ShowTree(mRoot); } private void ShowTree(RedBlackTreeNode<T> node) { if (node == null) { return; } ShowTree(node.LeftChild); string nodeColor = node.Color == RED ? "red" : "black"; string log; if (node.Parent != null) { log = node.Data + " " + nodeColor + " parent= " + node.Parent.Data; } else { log = node.Data + " " + nodeColor + " parent= null"; } //列印節點資料 Console.WriteLine(log); ShowTree(node.RightChild); } public class RedBlackTreeNode<T>{ public T Data { get; set; } public RedBlackTreeNode<T> LeftChild { get; set; } public RedBlackTreeNode<T> RightChild { get; set; } public RedBlackTreeNode<T> Parent { get; set; } public bool Color { get; set; } public RedBlackTreeNode(T value, bool color) { Data = value; LeftChild = null; RightChild = null; Color = color; } } } ///public class RedBlackTreeNode<T> //{ //資料 ///public T Data { get; set; } //左子節點// //public RedBlackTreeNode<T> LeftChild { get; set; } //右子節點 //public RedBlackTreeNode<T> RightChild { get; set; } //父節點 // public RedBlackTreeNode<T> Parent { get; set; } //該節點顏色 // public bool Color { get; set; } // public RedBlackTreeNode(T value, bool color) // { // Data = value; // LeftChild = null; // RightChild = null; // Color = color; // } //} }View Code
基於C++、Python的方法陸續更近。