Datawhale 数据结构与算法

Datawhale 数据结构与算法

1数组

  • 数组是一种常用的数据结构
  • 可以看作是线性表的推广
  • 数据元素多样,但必须属于同一数据类型

1.1 逻辑结构

数组是n(N>=1)个相同数据类型的数据元素的有限序列;

数组是具有固定格式和数量的数据有序集;

注意:在数据上不能进行插入、删除数据元素等操作

数组的操作:

  • 取值:读取给定一组下标对应的数据元素
  • 赋值:存储或修改给定一组下标对应的数据元素
  • 清空
  • 复制
  • 排序:数据元素排序(元素为可排序)
  • 反转:反转元素顺序

1.2 内存映像

数组是一种随机存储结构

有两种存储方法是:

  • 以行为主序
  • 以列为主序

1.3 C#中的数组

C#支持一维数组、多维数组、交错数组

数组在托管堆上分配空间,是引用类型;

常用方法
public abstract class Array : ICloneable, IList, ICollection, IEnumerable
{
public bool IsFixedSize{get;}
public iint Length{get;}

//获取 Array 的秩(维数)。
public int Rank { get; } 

//实现的 IComparable 接口,在.Array 中搜索特定元素。
public static int BinarySearch(Array array, object value); 

//实现的 IComparable<T>泛型接口,在 Array 中搜索特定元素。
public static int BinarySearch<T>(T[] array, T value); 

//实现 IComparable 接口,在 Array 的某个范围中搜索值。
public static int BinarySearch(Array array, int index, int length,object value); 

//实现的 IComparable<T>泛型接口,在 Array 中搜索值。
public static int BinarySearch<T>(T[] array,  int index, int length, T value); 

//Array 设置为零、false 或 null,具体取决于元素类型。
public static void Clear(Array array, int index, int length); 

//System.Array 的浅表副本。
public object Clone(); 

//从第一个元素开始复制 Array 中的一系列元素 //到另一 Array 中(从第一个元素开始)。
public static void Copy(Array sourceArray,  Array destinationArray, int length);       

//将一维 Array 的所有元素复制到指定的一维 Array 中。
public void CopyTo(Array array, int index); 

//创建使用从零开始的索引、具有指定 Type 和维长的多维 Array。
public static Array CreateInstance(Type elementType,  params int[] lengths);

//返回 ArrayIEnumerator。
public IEnumerator GetEnumerator(); 

//获取 Array 指定维中的元素数。
public int GetLength(int dimension); 

//获取一维 Array 中指定位置的值。
public object GetValue(int index); 

//返回整个一维 Array 中第一个匹配项的索引。
public static int IndexOf(Array array, object value); 

//返回整个.Array 中第一个匹配项的索引。
public static int IndexOf<T>(T[] array, T value); 

//返回整个一维 Array 中后一个匹配项的索引。
public static int LastIndexOf(Array array, object value); 

//反转整个一维 Array 中元素的顺序。
public static void Reverse(Array array); 

//设置给一维 Array 中指定位置的元素。
public void SetValue(object value, int index); 

//对整个一维 Array 中的元素进行排序。
public static void Sort(Array array);
}

1.4 练习部分

练习一:利用动态数组解决数据存放问题

编写一段代码,要求输入一个整数N,用动态数组A来存放2~N之间所有5或7的倍数,输出该数组。

C#代码

static void Main(string[] args)
        {
            Console.WriteLine("N=");
            string str = Console.ReadLine();
            int n=0;
            if (int.TryParse(str, out n))
            {
                DArray<int> dArr = new DArray<int>();
                int j = 0;
                for (int i = 2; i <= n; i++)
                {
                    if (i%5 == 0 || i%7 == 0)
                    {
                        dArr.Append(i);
                    }
                }
                Console.Write(dArr);
            }
        }

练习二:托普利茨矩阵问题

https://leetcode-cn.com/problems/toeplitz-matrix/

如果一个矩阵的每一方向由左上到右下的对角线上具有相同元素,那么这个矩阵是托普利茨矩阵。

给定一个M x N的矩阵,当且仅当它是托普利茨矩阵时返回True。

public class Solution {
    public bool IsToeplitzMatrix(int[][] matrix)
    {
        for(int i = 0; i < matrix.Length - 1; i++)
        {
            for(int j = 0; j < matrix[i].Length - 1; j++)
            {
                if(matrix[i][j] != matrix[i+1][j+1])
                {
                    return false;
                }
            }
        }
        return true;
    }
}

练习三:三数之和

https://leetcode-cn.com/problems/3sum/

给定一个包含 n 个整数的数组nums,判断nums中是否存在三个元素a,b,c,使得a + b + c = 0?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

public class Solution {
    public IList<IList<int>> ThreeSum(int[] nums) {
            Array.Sort(nums);
            IList<IList<int>> all = new List<IList<int>>();

            int len = nums.Length;
            int x = 0, y, z;
            List<int> li;
            for (; x < len-1; x++)
            {
                if (nums[x] > 0)
                    break;
                if (x > 0 && nums[x] == nums[x - 1])
                    continue;
                z = x + 1;
                y=len - 1;
                while (z < y )
                {
                    int num = nums[x] + nums[y] + nums[z];
                    if (num > 0)
                        while (y>z && nums[y] == nums[--y]) ;
                    else if (num < 0)
                        while ( y>z &&  nums[z] == nums[++z]) ;
                    else
                    {
                        li = new List<int>();
                        li.Add(nums[x]);
                        li.Add(nums[z]);
                        li.Add(nums[y]);
                        all.Add(li);
                        while(y>x && nums[y]==nums[--y]);
                    }
                }
            }
            return all;
    }
}

2顺序表和链表

2.1 线性表

  • 线性表是最简单、最基本、最常用的数据结构;
  • 特定是数据元素之间存在一对一的线性关系;
  • 除开始、结束数据元素外,其他数据元素都有且仅有一个直接前驱和直接后续;
  • 两种存储结构:顺序存储和链式存储

2.2 顺序表

顺序存储的线性表叫顺序表,

表中存储单元连续,C#中用数组来实现

2.3 链表

链式存储的线性表是链表,存储单元不一定连续

在一个节点中,还有数据域存放数据元素本身信息,还有引用域存储其相邻的数据元素的地址信息。

  • 单链表:只有一个引用域,村方其后直接后续节点的地址信息
  • 双向链表:有两个引用域,存放其直接前驱节点和直接后续节点的地址信息
  • 循环链表:最后一个及地点的引用域存放其头引用的值。

2.4 顺序表与链表

  • 顺序表:随机存储,查找效率高,但插入和删除需要移动大量元素,效率低
  • 联播啊:存储空间不要求连续,插入、删除效率高,但查找需要从头引用遍历链表,效率低。

2.5 练习题

2.5.1 合并两个有序链表

https://leetcode-cn.com/problems/merge-two-sorted-lists/

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

public class Solution {
    public ListNode MergeTwoLists(ListNode l1, ListNode l2) {
        var newList = new ListNode(0);
        //链表的节点
        var node = newList;
        while(l1 != null && l2 != null)
        {
            if(l1.val <l2.val)
            {
                node.next = l1;
                l1 = l1.next;
            }
            else
            {
                node.next = l2;
                l2 = l2.next;
            }
            //更新节点
            node = node.next;
        }
        if(l1 != null)
            node.next = l1;

        if(l2 != null)
            node.next = l2;
        return newList.next;
    }
}

2.5.2 删除链表的倒数第N个节点

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

给定一个链表,删除链表的倒数第?n?个节点,并且返回链表的头结点。

    public ListNode RemoveNthFromEnd(ListNode head, int n) {
        int len = 1;
        var aNode = head;
        var bNode = head;

        while(aNode.next != null)
        {
            if(len > n)
            {
                bNode = bNode.next;
            }
            aNode = aNode.next;
            len++;
        }
        if(n ==len) return head.next;
        bNode.next = bNode.next.next;
        return head;
    }

2.5.3 旋转链表

https://leetcode-cn.com/problems/rotate-list/

给定一个链表,旋转链表,将链表每个节点向右移动k个位置,其中k是非负数。

   public ListNode RotateRight(ListNode head, int k)
    {
        if (head == null || head.next == null) return head;
        ListNode aNode = head;
        int len = 0;
        while(k > 0 && aNode != null)
        {
            aNode = aNode.next;
            len++;
            k--;
        }
        if(aNode == null)
        {
            k = k % len; //余数
            aNode = head;
            while(k > 0)
            {
                aNode = aNode.next;
                k--;
            }
        }
        ListNode bNode = head;
        if(k == 0)
        {
            while(aNode.next != null)
            {
                aNode = aNode.next;
                bNode = bNode.next;
            }
        }
        aNode.next = head;
        head = bNode.next;
        bNode.next = null;
        return head;
    }

3. 栈与递归

3.1 栈 Stack

是操作限定在表的尾端进行的线性表

表为进行插入、删除等操作

表尾称为栈顶top,另一端固定叫栈底bottom

没有数据元素的栈叫空栈Empty Stack

S = (a1,a2,...,an)

a1 栈底元素

an栈顶元素

出入栈顺序:先进后出,后进先出

S=(D,R) D是数据元素的有限集合,R是数据元素之间关系的有限集合

栈的操作:栈顶插入、删除元素,取栈顶元素,判断栈是否为空

3.2 顺序栈

用连续的存储空间来存储栈中的数据元素,称为顺序栈 Sequence Stack 。一维数据存放顺序栈中的数据元素。

public class SeqStack<T>:IStack<T>{
    private int maxsize;
    private T[] data;  //顺序栈中的数据元素
    private int top;  //顺序栈的栈顶
}

3.3 链栈

链式存储的栈成为链栈Linked Stack

通常用单链表来表示,是单链表的简化

栈顶设在链表头部,不需要头结点

链栈节点类Node 的实现

public class Node<T>
{
    private T data;  //数据域
    private Node<T> next;  //引用域
}

链栈类LinkStack 的实现

public class LinkStack<T>:IStack<T>{
    private Node<T> top;  //栈顶指示器
    private int num; // 栈中结点的个数
}

链栈的基本操作:

  • 求链栈的长度 GetLength()
  • 清空操作Clear()
  • 判断链栈是否为空IsEmpty()
  • 入栈操作Push()
  • 出战操作Pop()
  • 获取链顶结点的值GetTop()

3.4 递归

一个算法直接调用自己或间接调用自己,就称这个算法是诋毁Recursive.

  • 调用方式不同:直接递归Direct Recursion、简介递归 Indirect Recursion
  • 必须有两个部分:初始部分、递归部分
  • 递归调用的理解:通过一系列的自身调用,达到某一终止条件后,在按照嗲用路线逐步返回。

3.5 练习题

3.5.1

根据要求完成车辆重排的程序代码

假设一列货运列车共有n节车厢,每节车厢将停放在不同的车站。假定n个车站的编号分别为1n,货运列车按照第n站至第1站的次序经过这些车站。车厢的编号与它们的目的地相同。为了便于从列车上卸掉相应的车厢,必须重新排列车厢,使各车厢从前至后按编号1n的次序排列。当所有的车厢都按照这种次序排列时,在每个车站只需卸掉最后一节车厢即可。

我们在一个转轨站里完成车厢的重排工作,在转轨站中有一个入轨、一个出轨和k个缓冲铁轨(位于入轨和出轨之间)。图(a)给出一个转轨站,其中有k个(k=3)缓冲铁轨H1H2?和H3。开始时,n节车厢的货车从入轨处进入转轨站,转轨结束时各车厢从右到左按照编号1n的次序离开转轨站(通过出轨处)。在图(a)中,n=9,车厢从后至前的初始次序为5,8,1,7,4,2,9,6,3。图(b)给出了按所要求的次序重新排列后的结果。

编写算法实现火车车厢的重排,模拟具有n节车厢的火车“入轨”和“出轨”过程。

    class Program
    {
        static void Main(string[] args)
        {
            int[] h = new int[]{5, 3, 6, 8, 4, 1, 2,9, 7};
            int k = 3;
            bool result = Stack.RailRoad(h, k);

            while (result == false)
            {
                Console.WriteLine("缓冲轨道数量不满足使用,请输入数字扩充。");
                k = k + Convert.ToInt32(Console.ReadLine());
                result = Stack.RailRoad(h, k);
            }

        }
    }
    /// <summary>
    /// 车厢重排算法
    /// 使用入出栈
    /// </summary>
    public static class Stack
    {
       public  static bool RailRoad(int[] h,int k)
        {
            int n = h.Length;
            int noOut = 1;
            Stack<int>[] stacks = new Stack<int>[k];
            for (int i = 0; i < k; i++)
                stacks[i] = new Stack<int>();

            for(int i = 0; i<n;i++)
            {
                if (noOut == h[i])
                {
                    //符合出轨序号,直接进入出轨
                    Console.WriteLine("直接出轨:车厢号{0}直接出轨", h[i]);
                    noOut++;
                    //判断缓冲轨是否有可以出轨车厢
                    while (OutPut(stacks, noOut))
                        noOut++;
                }
                else
                {
                    //存入缓冲轨
                    if (!Hold(stacks, h[i]))
                        return false;
                }
            }

            return true;
        }
        #region 车厢入缓冲轨
        /// <summary>
        /// 车厢入缓冲轨
        /// </summary>
        /// <param name="stacks">缓冲轨栈</param>
        /// <param name="no">车厢号</param>
        /// <returns></returns>
        static bool Hold(Stack<int>[] stacks, int no)
        {
            int m = 0;  //最佳缓冲轨,默认第一轨
            int top = 0; //最佳缓冲轨栈顶元素,默认第一轨栈顶元素
            for (int i = 0; i < stacks.Length; i++)
            {
                if (stacks[i].Count == 0)
                {
                    m = top > no ? m: i;
                    top = no;
                    break;
                }
                else
                {
                    if (no < stacks[i].Peek())
                    {
                        if ( top == 0 || top > stacks[i].Peek())
                        {
                            top = stacks[i].Peek();
                            m = i;
                        }
                    }
                }
            }
            if (top == 0)
                return false;

            stacks[m].Push(no);
            Console.WriteLine("入缓冲轨:车厢号{0}入缓冲轨{1}", no, m + 1);

            return true;
        }
        #endregion

        #region 车厢出缓冲轨
        /// <summary>
        /// 车厢出缓冲轨
        /// </summary>
        /// <param name="stacks"></param>
        /// <param name="no"></param>
        /// <returns>是否有车厢从缓冲轨出</returns>
        static bool OutPut(Stack<int>[] stacks,int no)
        {
            for (int i = 0; i < stacks.Length; i++)
            {
                if (stacks[i].Count == 0)
                    continue;
                if(no == stacks[i].Peek())
                {
                    stacks[i].Pop();
                    Console.WriteLine("出缓冲轨:车厢号{0}出缓冲轨{1}", no, i + 1);

                    return true;
                }
            }
            return false;
        }
        #endregion
    }

4 队列 Queue

4.1 队列的定义及运算

  • 是插入操作限定在表的尾部而其他操作限定在表的头部进行的线性表
  • 当队列中没有数据元素是成为空队列Empty Queue
  • 队列的操作是先进先出or 后进后出 (First In First Out / Last In Last Out )
  • Q = (D,R) D 是数据元素的有限集合,R是数据元素之间关系的有限集合
  • 操作:

    队尾插入、队头删除、去队头元素和判断队列是否为空

  • 和栈一样,队列的元素是定义在逻辑结构层次上,而运算的具体实现是建立在物理存储结构层次上
  • C#中,队列是从IEnumerable、ICollection和IEnumberable 接口继承而来
public interface IQueu<T>{
      int GetLength();
      bool IsEmpty();
      void Clear();
      void In(T item);  //将值为item的新数据元素添加到队尾
      T Out();     //将队头元素从队列中取出,队列发生变化
      T GetFront();   //取队头元素的值,队列不发生变化
}

4.2 顺序队列

用一片连续的存储空间来存储队列中的数据元素,这样的队列成为顺序队列 Sequence Queue ,类似与顺序栈。

  • front 队头指示器,rear 队尾指示器
  • 当队列为空时,front = rear = -1
  • 数据元素入队,rear + 1
  • 数据元素出队,front +1
  • rear 达到数据上限而front 为-1时,队列为满

4.3 循环顺序队列

将顺序队列堪称时首位相接的循环结构,头尾指示器的关系不变

public class CSeqQueue<T>:IQueue<T>{
  private int maxsize;
  private T[] data;
  private int front;
  private int rear;

  public int GetLength()
  {
    return (rear - front + maxsize)%maxsize;
  }
  public void Clear()
  {
     front = rear = -1;
  }
  public bool IsEmpty()
  {
    if(front == rear)
    {
      return true;
    }
    else
    {
      return false;
    }
  }
  public bool IsFull()
  {
    // (rear + 1)% maxsize 等于 front
  }
  public void In(T item)
  {
    // data[++rear] = item;
  }
  public T Out()
  {
    T tmp = default(T);
    if(IsEmpty())
    {
      Console.WriteLine("Queue is empty");
      return tmp;
    }
    tmp = data[++front];
    return tmp;
  }
  public T GetFront()
  {
    //IsEmpty判断
    return data[front+1];
  }

4.4 链队列

队列的另一种存储方式时链式存储,称为链队列 Linked Queue

  • 通常用单链表来表示,它的实现时单链表的简化
  • 链队列长度:GetLength() //return num
  • 清空操作:Clear() //front = rear = null; num = 0;
  • 链队列是否为空 IsEmpty() // front == rar && num == 0
  • 入队操作:In()
Node<T> q = new Node<T>(item);
if(rear == null)
{
 front = rear = q;
}
else
{
 rear.Next = q;
 rear = q;
}
num++;
  • 出队操作:Out()
public T Out()
{
  if(IsEmpty())
  {
    Console.WriteLine("Queue is empty");
    return default(T);
  }
  Node<T> p = front;
  front = front.Next;
  --num;
  return p.Data;
}

  • 获取链队列头结点的值:GetFront() // return front.Data;

4.5 练习

4.5.1 模拟银行服务完成程序代码。

目前,在以银行营业大厅为代表的窗口行业中大量使用排队(叫号)系统,该系统完全模拟了人群排队全过程,通过取票进队、排队等待、叫号服务等功能,代替了人们站队的辛苦。

排队叫号软件的具体操作流程为:

顾客取服务序号

当顾客抵达服务大厅时,前往放置在入口处旁的取号机,并按一下其上的相应服务按钮,取号机会自动打印出一张服务单。单上显示服务号及该服务号前面正在等待服务的人数。

服务员工呼叫顾客 服务员工只需按一下其柜台上呼叫器的相应按钮,则顾客的服务号就会按顺序的显示在显示屏上,并发出“叮咚”和相关语音信息,提示顾客前往该窗口办事。当一位顾客办事完毕后,柜台服务员工只需按呼叫器相应键,即可自动呼叫下一位顾客。

编写程序模拟上面的工作过程,主要要求如下:

程序运行后,当看到“请点击触摸屏获取号码:”的提示时,只要按回车键,即可显示“您的号码是:XXX,您前面有YYY位”的提示,其中XXX是所获得的服务号码,YYY是在XXX之前来到的正在等待服务的人数。

用多线程技术模拟服务窗口(可模拟多个),具有服务员呼叫顾客的行为,假设每个顾客服务的时间是10000ms,时间到后,显示“请XXX号到ZZZ号窗口!”的提示。其中ZZZ是即将为客户服务的窗口号。

IQueue接口、循环顺序队列、链队列

 interface IQueue<T>
    {
        int GetLength();
        bool IsEmpty();
        void Clear();
        void In(T item);
        T Out();
        T GetFront();
    }
    #region 循环顺序队列
    /// <summary>
    /// 循环顺序队列
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CSeqQueue<T> : IQueue<T>
    {
        private int maxsize;
        private T[] data;
        private int front;
        private int rear;
        public T this[int index]
        {
            get
            {
                return data[index];
            }
            set
            {
                data[index] = value;
            }
        }
        public int MaxSize { get { return maxsize; } set { maxsize = value; } }
        public int Front { get { return front; } set { front = value; } }

        public int Rear { get { return rear; } set { rear = value; } }

        public CSeqQueue(int size)
        {
            data = new T[size];
            maxsize = size;
            front = rear = -1;
        }

        public int GetLength()
        {
            return (rear - front + maxsize) % maxsize;
        }

        public void Clear()
        {
            front = rear = -1;
        }
        public bool IsEmpty()
        {
            if (front == rear)
                return true;
            return false;
        }
        public bool IsFull()
        {
            if ((rear + 1) % maxsize == front)
                return true;
            return false;
        }
        public void In(T item)
        {
            if (IsFull())
            {
                Console.WriteLine("Queue is Full");
                return;
            }
            data[++rear] = item;
        }

        public T Out()
        {
            T tmp = default(T);
            if (IsEmpty())
            {
                Console.WriteLine("Queue is empty");
                return tmp;
            }
            tmp = data[++front];
            return tmp;
        }
        public T GetFront()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Queue is empty");
                return default(T);
            }
            return data[front + 1];
        }
    }
    #endregion

    #region 链队列结点类
    /// <summary>
    /// 链队列结点类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Node<T>
    {
        private T data;
        private Node<T> next;

        public Node(T val, Node<T> p)
        {
            data = val;
            next = p;
        }
        public Node(Node<T> p)
        {
            next = p;
        }
        public Node(T val)
        {
            data = val;
            next = null;
        }
        public Node()
        {
            data = default(T);
            next = null;
        }

        public T Data
        {
            get { return data; }
            set { data = value; }
        }
        public Node<T> Next
        {
            get { return next; }
            set { next = value; }
        }
    }
    #endregion

    #region 链队列
    /// <summary>
    /// 链队列
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class LinkQueue<T> : IQueue<T>
    {
        private Node<T> front;
        private Node<T> rear;
        private int num;  //队列结点个数

        public Node<T> Front { get { return front; } set { front = value; } }
        public Node<T> Rear { get { return rear; } set { rear = value; } }
        public int Num { get { return num; } set { num = value; } }
        public LinkQueue()
        {
            front = rear = null;
            num = 0;
        }
        public int GetLength()
        {
            return num;
        }
        public void Clear()
        {
            front = rear = null;
            num = 0;
        }
        public bool IsEmpty()
        {
            if((front == rear) && (num == 0))
                return true;
            return false;
        }
        public void In(T item)
        {
            Node<T> q = new Node<T>(item);
            if(rear == null)
            {
                front = rear = q;
            }
            else
            {
                rear.Next = q;
                rear = q;
            }
            num++;
        }

        public T Out()
        {
            if(IsEmpty())
            {
                Console.WriteLine("Queue is empty");
                return default(T);
            }
            Node<T> p = front;
            front = front.Next;
            if(front == null)
            {
                rear = null;
            }
            --num;
            return p.Data;
        }
        public T GetFront()
        {
            if(IsEmpty())
            {
                Console.WriteLine("Queue is empty");
                return default(T);
            }
            return front.Data;
        }
    }
    #endregion
    public interface IBankQueue:IQueue<int>
    {
        int GetCallNum();
        int MaxSize { get; }
    }

    public  class LinkBankQueue:LinkQueue<int>,IBankQueue
    {
        public int CallNumber { get; set; }
        public int MaxSize { get; }

        public LinkBankQueue()
        {
            MaxSize = default(int);
            CallNumber = 0;
        }
        public int GetCallNum()
        {
            if(IsEmpty() && CallNumber ==0)
            {
                CallNumber = 1;
            }
            else
            {
                CallNumber++;
            }
            return CallNumber;
        }
    }

    public class CSeqBankQueue:CSeqQueue<int>,IBankQueue
    {
        public int Callnumber { get; private set; }
        //public int MaxSize { get; }
        public CSeqBankQueue(int size):base(size)
        {
            Callnumber = 0;
        }
        public int GetCallNum()
        {
            if (IsEmpty() && Callnumber == 0)
                Callnumber = 1;
            else
                Callnumber++;
            return Callnumber;
        }
    }

    public class ServiceWindow
    {
        public IBankQueue Bankq { get; set; }
        public void service()
        {
            while(true)
            {
                lock(Bankq)
                {
                    Thread.Sleep(10000);
                    if(!Bankq.IsEmpty())
                    {
                        Console.WriteLine();
                        Console.WriteLine("{2}:请{0}号到{1}号窗口", Bankq.GetFront(), Thread.CurrentThread.Name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                        Bankq.Out();
                    }
                }
            }
        }
    }

main

   class Program
    {
        /// <summary>
        /// 银行排队叫号
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            try
            {
                IBankQueue bankQueue = null;
                Console.WriteLine("请选择存储结构类型:1.顺序队列,2.链队列");
                string flag = Console.ReadLine();
                switch(flag)
                {
                    case "1":
                        Console.WriteLine("请输入队列可容纳人数:");
                        int count = Convert.ToInt32(Console.ReadLine());
                        bankQueue = new CSeqBankQueue(count);
                        break;
                    case "2":
                        bankQueue = new LinkBankQueue();
                        break;
                }
                int windowsnum = 5;
                ServiceWindow[] serviceWindows = new ServiceWindow[windowsnum];
                Thread[] serviceThread = new Thread[windowsnum];
                for (int i = 0; i < windowsnum; i++)
                {
                    serviceWindows[i] = new ServiceWindow();
                    serviceWindows[i].Bankq = bankQueue;
                    serviceThread[i] = new Thread(serviceWindows[i].service);
                    serviceThread[i].Name = (i + 1).ToString();
                    serviceThread[i].Start();
                }

                while(true)
                {
                    Console.WriteLine("点击获取号码:");
                    Console.ReadLine();
                    if (bankQueue != null && (bankQueue.GetLength() < bankQueue.MaxSize || flag == "2"))
                    {
                        int callnumber = bankQueue.GetCallNum();
                        Console.WriteLine("{2}:您的号码时:{0},前面还有{1}位等待。", callnumber, bankQueue.GetLength(),DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                        bankQueue.In(callnumber);
                    }
                    else
                        Console.WriteLine("请重试");
                    Console.WriteLine();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + ex.StackTrace);
            }
        }

5 字符串

5.1 概念

  • 串由n(n>=0)字符组成的有限序列
  • 连续存储
  • C#中,字符串创建后不可修改

5.2 基本操作

  • 串长:GetLength()
  • 串比较:Compare(StringDS s)
  • 子串:SubString(int index, int len)
  • 串连接:Concat(StringDS s)
  • 串插入:Insert(int index, StringDS s)
  • 串删除:Delete(int index, int len)
  • 串定位:Index(StringDS s)

5.3 C#中的串

表示一个恒定不变的字符序列集合

不能被其他类继承,直接继承自object

是引用类型

在托管堆上而不是在吸纳从的堆栈上分配空间

对串的所有操作的结果都是生成了新串而没有改变原串

public sealed class String:IComparable,ICloneable,IConvertible,IComparable<string>,IEnumerable<char>,IEnumerable,IEquatable<string>
{
  public String(char[] value);
  public static bool operator !=(string a, string b);
  public static bool operator ==(string a, string b);
  public object Clone();
  public static int Compare(string strA, string strB);
  public iint Compare(string strB);
  public static int CompareOrdinal(string strA,string strB);
  public static string Concat(string str0, string str1);
  public static string Copy(string str);
  public void CopyTo(iint sourceIndex,char[] destination, int   destinationIndex, int count);
  public bool StartsWith(string value, StringComparison comparisonType);
  public bool EndsWith(string value, StriingComparison comparisonType);
  public static string Format(string format,object arg0);
  public int IndexOf(string value);
  public string Insert(int startIndex, string value);
  public string Remove(int startIndex, int count);
  public string Replace(string oldVlaue,string newValue);
  public string Substring(int startIndex, int length);
  public string ToLower();
  public string ToUpper();
  public string Trim(params char[] trimChars);
}

5.4 练习题

5.4.1 给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。

https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

输入: "abcabcbb"
输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

    public int LengthOfLongestSubstring(string s)
     {
        int start = 0;
        int len = 0 ;
        int[] str = new int[128];
        for(int i = 0;i<s.Length;i++)
        {
            start = Math.Max(str[s[i]],start);
            len = Math.Max(len,i - start +1);
            str[s[i]] = i +1;
        }
        return len;
    }

5.4.2 给定一个字符串s和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

输入:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
输出:[0,9]

解释:

从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。输出的顺序不重要, [9,0] 也是有效答案。

from collections import Counter
        if not s or not words:return []
        one_word = len(words[0])
        all_len = len(words) * one_word
        n = len(s)
        words = Counter(words)
        res = []
        for i in range(0, n - all_len + 1):
            tmp = s[i:i+all_len]
            c_tmp = []
            for j in range(0, all_len, one_word):
                c_tmp.append(tmp[j:j+one_word])
            if Counter(c_tmp) == words:
                res.append(i)
        return res

5.4.3 替换子串得到平衡字符串

https://leetcode-cn.com/problems/replace-the-substring-for-balanced-string/

有一个只含有‘Q‘,?‘W‘,?‘E‘,‘R‘四种字符,且长度为?n的字符串。假如在该字符串中,这四个字符都恰好出现n/4次,那么它就是一个「平衡字符串」。

给你一个这样的字符串?s,请通过「替换一个子串」的方式,使原字符串?s变成一个「平衡字符串」。你可以用和「待替换子串」长度相同的任何其他字符串来完成替换。

请返回待替换子串的最小可能长度。

如果原字符串自身就是一个平衡字符串,则返回 0。

示例1:

输入:s = "QWER"
输出:0
解释:s 已经是平衡的了。

示例2:

输入:s = "QQWE"
输出:1
解释:我们需要把一个 ‘Q‘ 替换成 ‘R‘,这样得到的 "RQWE" (或 "QRWE") 是平衡的。

示例3:

输入:s = "QQQW"
输出:2
解释:我们可以把前面的 "QQ" 替换成 "ER"。

示例4:

输入:s = "QQQQ"
输出:3
解释:我们可以替换后 3 个 ‘Q‘,使 s = "QWER"。
 public int BalancedString(string s) {
        int n = s.Length;
        int num = n/ 4 ;  //平均次数
        if(n < 4 || n % 4 >0)
            return 0;

        Dictionary<char, int> dict = new Dictionary<char, int>();
        string copyS = String.Copy(s);

        int l = copyS.Length;
        while(copyS.Length >0)
        {
            char c =Convert.ToChar(copyS.Substring(0,1));
            string str = copyS.Replace(c.ToString(),"");
            if(l-str.Length > num)
                dict.Add(c,l-str.Length-num);
            l = str.Length;
        }

        int result = 0;
        Queue<int> queue = new Queue<int>();
        for(int i = 0;i<s.Length;i++)
        {
            if(dict.ContainsKey(s[i]))
            {
                dict[s[i]]--;
                queue.Enqueue(i);

                bool check= true;
                foreach(var v in dict.Values)
                {
                    if(v>0)
                    {
                        check = false;
                        break;
                    }
                }

                if(check)
                {
                    while(queue.Count != 0)
                    {
                        int j = queue.Peek();
                        queue.Dequeue();

                        result = Math.Min(result, i - j +1);
                        dict[s[j]]++;
                        if(dict[s[j]] >0)
                        {
                            break;
                        }
                    }
                }
            }
        }
        return result;
    }

原文地址:https://www.cnblogs.com/liying-yds/p/12656275.html

时间: 2024-07-31 19:37:16

Datawhale 数据结构与算法的相关文章

数据结构与算法C++描述学习笔记1、辗转相除——欧几里得算法

前面学了一个星期的C++,以前阅读C++代码有些困难,现在好一些了.做了一些NOI的题目,这也是一个长期的目标中的一环.做到动态规划的相关题目时发现很多问题思考不通透,所以开始系统学习.学习的第一本是<数据结构与算法C++描述>第三版,边学边做一些笔记.所以这些笔记中的代码有很多将会非常简单,甚至可能只有一个记录或者结论. 辗转相除法用来求两个整数的最大公约数,即能同时整除两个数的最大整数.程序如下: int gdc(int m,int n){ int rem; while(n!=0){ //

数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树

[本文谢绝转载,原文来自http://990487026.blog.51cto.com] 树 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树 二叉树的创建,关系建立 二叉树的创建,关系建立2 三叉链表法 双亲链表: 二叉树的遍历 遍历的分析PPT 计算二叉树中叶子节点的数目:使用全局变量计数器 计算二叉树中叶子节点的数目:不使用全局变量计数器 无论是先序遍历,中序遍历,后序遍历,求叶子的数字都不变;因为本质都是一样的,任何一个节点都会遍历3趟 求二叉树的高度 二叉树的拷

数据结构与算法之线性表

前言 上一篇<数据结构和算法之时间复杂度和空间复杂度>中介绍了时间复杂度的概念和常见的时间复杂度,并分别举例子进行了一一说明.这一篇主要介绍线性表. 线性表属于数据结构中逻辑结构中的线性结构.回忆一下,数据结构分为物理结构和逻辑结构,逻辑结构分为线性结构.几何结构.树形结构和图形结构四大结构.其中,线性表就属于线性结构.剩余的三大逻辑结构今后会一一介绍. 线性表 基本概念 线性表(List):由零个或多个数据元素组成的有限序列. 注意: 1.线性表是一个序列. 2.0个元素构成的线性表是空表.

数据结构与算法(刺猬书)读书笔记----目录

最近在抓底层的语言基础,以前对数据结构和算法并没有太大感觉,但越往深处学就越觉得这些基础真的是要牢牢掌握住.一个简简单单的数组,深究起来都有很多学问.所以打算写个一系列的读书笔记,好好梳理一下这一块的基础知识.这本书是<数据结构预算法JavaScript描述>,是基于JavaScript的.里面大致介绍了数组.列表.栈.队列.链表.散列.集合及各种常见基础算法.作为基础读物算是很全面的.这系列读书笔记也将会跟着书里的顺序一章章的进行梳理.整个过程计划耗时2-3个月,每周更新一到两张,更新的笔记

Java数据结构和算法(一):综述

数据结构和算法能起到什么作用? 数据结构是指数据在计算机内存空间或磁盘中的组织形式.数据结构包括数组.链表.栈.二叉树.哈希表等等.算法对这些结构中的数据进行各种处理,例如,查找一条特殊的数据项或对数据进行排序. 数据结构的概述 数据结构 优点 缺点 数组 插入快,如果知道下标,可以非常快地存取 查找慢,删除慢,大小固定 有序数组 比无序数组查找快 删除和插入慢,大小固定 栈 提供后进先出方式的存取 存取其他项很慢 队列 提供先进先出方式的存取 存取其他项很慢 链表 插入快,删除快 查找慢 二叉

数据结构与算法系列研究四——数组和广义表

稀疏矩阵的十字链表实现和转置 一.数组和广义表的定义 数组的定义1:一个 N 维数组是受 N 组线性关系约束的线性表.           二维数组的逻辑结构可形式地描述为:           2_ARRAY(D,R)              其中 D={aij} | i=0,1,...,b1-1; j=0,1,...,b2-1;aij∈D0}              R={Row,Col}              Row={<aij,ai,j+1>|0<=i<=b1-1;

javascript数据结构与算法--二叉树(插入节点、生成二叉树)

javascript数据结构与算法-- 插入节点.生成二叉树 二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * * * */ /*用来生成一个节点*/ function Node(data, left, right) { this.data = data;//节点存储的数据 this.left = left; this.right = right; this.show = show; } function sh

数据结构与算法 第四次实验报告 图

数据结构与算法 第四次实验报告 姓名:许恺 学号:2014011329 班级:计算机14-1     中国石油大学(北京)计算机科学与技术系 1.图的定义,文件为"Graph.h" #ifndef GRAPH_H//定义头文件 #define GRAPH_H #include<string>//引入标准库中的头文件 using namespace std; const int MaxSize=12; struct ArcNode//定义边表结点 { int adjvex;/

数据结构与算法1

数据结构与算法(一),概述 转载请注明出处:http://www.cnblogs.com/wangyingli/p/5919297.html 数据结构学了有一年的时间了,但是一直没有好好的总结一下,现在回想起来,感觉好像都不怎么记得了.所以接下来一段时间我将重新学习一下,算是温故而知新了.本着「分享是一种美德」的精神,我将把我的学习总结记录下来,并与大家分享. 本节的主要内容有: 一.数据结构 1.定义 2.关于数据结构的几个术语 3.逻辑结构与物理结构 二.抽象数据类型 三.算法 四.算法的复