日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#数据结构与算法总结

發布時間:2024/1/8 C# 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#数据结构与算法总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

線性表

線性表是最簡單、最基本、最常用的數據結構。線性表是線性結構的抽象(Abstract),線性結構的特點是結構中的數據元素之間存在一對一的線性關系。這種一對一的關系指的是數據元素之間的位置關系,即:

  • 除第一個位置的數據元素外,其它數據元素位置的前面都只有一個數據元素;
  • 除最后一個位置的數據元素外,其它數據元素位置的后面都只有一個元素。也就是說,數據元素是一個接一個的排列。因此,可以把線性表想象為一種數據元素序列的數據結構。
  • 線性表就是位置有先后關系,一個接著一個排列的數據結構。

    CLR中的線性表

    c# 1.1 提供了一個非泛型接口IList接口,接口中的項是object,實現了IList解扣子的類有ArrayList,ListDictionary,StringCollection,StringDictionary.

    c# 2.0 提供了泛型的IList接口,實現了List接口的類有List

    線性表的接口定義

    interface IListDS<T> {int GetLength(); //求長度void Clear(); //清空操作bool IsEmpty();//判斷線性表是否為空void Add(T item);//附加操作void Insert(T item, int index); //插入操作T Delete(int index); //刪除操作T this[int index] { get; }//定義一個索引器 獲取元素T GetEle(int index);//取表元int Locate(T value);//按值查找 }

    線性表的實現方式

    線性表的實現方式有下面幾種

    • 順序表
    • 單鏈表
      • 雙向鏈表
      • 循環鏈表

    順序表

    在計算機內,保存線性表最簡單、最自然的方式,就是把表中的元素一個接一個地放進順序的存儲單元,這就是線性表的順序存儲(Sequence Storage)。線性表的順序存儲是指在內存中用一塊地址連續的空間依次存放線性表的數據元素,用這種方式存儲的線性表叫順序表(Sequence List),如圖所示。順序表的特點是表中相鄰的數據元素在內存中存儲位置也相鄰。

    順序表的存儲

    假設順序表中的每個數據元素占w個存儲單元,設第i個數據元素的存儲地址為Loc(ai),則有:
    Loc(ai)= Loc(a1)+(i-1)*w 1≤i≤n式中的Loc(a1)表示第一個數據元素a1的存儲地址,也是順序表的起始存儲地址,稱為順序表的基地址(Base Address)。也就是說,只要知道順序表的基地址和每個數據元素所占的存儲單元的個數就可以求出順序表中任何一個數據元素的存儲地址。并且,由于計算順序表中每個數據元素存儲地址的時間相同,所以順序表具有任意存取的特點。(可以在任意位置存取東西)
    C#語言中的數組在內存中占用的存儲空間就是一組連續的存儲區域,因此,數組具有任意存取的特點。所以,數組天生具有表示順序表的數據存儲區域的特性。

    順序表的實現

    class SeqList<T> : IListDS<T> {private T[] data;//用來存儲數據private int count = 0;public SeqList(int size){data = new T[size];}public SeqList() : this(10){}public T this[int index]{get{return GetEle(index);}}public void Add(T item){if (count == data.Length)//當前數組已經存滿{Console.WriteLine("當前順序表已存滿,不允許再存入");}else{data[count] = item;count++;}}public void Clear(){count = 0;}public T GetEle(int index){if (index >= 0 && index <= count - 1){return data[index];}else{Console.WriteLine("超出順序表索引范圍");return default(T);}}public int GetLength(){return count;}public void Insert(T item, int index){for (int i = count - 1; i >= index; i--){data[i + 1] = data[i];}data[index] = item;count++;}public T Delete(int index){T temp = data[index];for (int i = index + 1; i < count; i++){data[i - 1] = data[i];}count--;return temp;}public bool IsEmpty(){return count == 0;}public int Locate(T value){for (int i = 0; i < count; i++){if (data[i].Equals(value)){return i;}}return -1;} }

    單鏈表

    順序表是用地址連續的存儲單元順序存儲線性表中的各個數據元素,邏輯上相鄰的數據元素在物理位置上也相鄰。因此,在順序表中查找任何一個位置上的數據元素非常方便,這是順序存儲的優點。但是,在對順序表進行插入和刪除時,需要通過移動數據元素來實現,影響了運行效率。線性表的另外一種存儲結構——鏈式存儲(Linked Storage),這樣的線性表叫鏈表(Linked List)。鏈表不要求邏輯上相鄰的數據元素在物理存儲位置上也相鄰,因此,在對鏈表進行插入和刪除時不需要移動數據元素,但同時也失去了順序表可隨機存儲的優點。

    單鏈表的存儲

    鏈表是用一組任意的存儲單元來存儲線性表中的數據元素(這組存儲單元可以是連續的,也可以是不連續的)。那么,怎么表示兩個數據元素邏輯上的相鄰關系呢?即如何表示數據元素之間的線性關系呢?為此,在存儲數據元素時,除了存儲數據元素本身的信息外,還要存儲與它相鄰的數據元素的存儲地址信息。這兩部分信息組成該數據元素的存儲映像(Image),稱為結點(Node)。把存儲據元素本身信息的域叫結點的數據域(Data Domain),把存儲與它相鄰的數據元素的存儲地址信息的域叫結點的引用域(Reference Domain)。因此,線性表通過每個結點的引用域形成了一根“鏈條”,這就是“鏈表”名稱的由來。
    如果結點的引用域只存儲該結點直接后繼結點的存儲地址,則該鏈表叫單鏈表(Singly Linked List)。把該引用域叫 next。單鏈表結點的結構如圖所示,圖中 data 表示結點的數據域。

    鏈式存儲結構

    下圖是線性表(a1,a2,a3,a4,a5,a6)對應的鏈式存儲結構示意圖。

    另外一種表示形式
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-D01GDnI3-1581864714521)(https://s1.ax1x.com/2018/12/26/F2lFwn.jpg)]

    單鏈表節點定義

    class Node<T> {private T data;private Node<T> next;public Node(){data = default(T);next = null;}public Node(T value){this.data = value;this.next = null;}public Node(T value, Node<T> next){this.data = value;this.next = next;}public Node(Node<T> next){this.next = next;}public T Data{get { return data; }set { data = value; }}public Node<T> Next{get { return next; }set { next = value; }} }

    單鏈表實現

    class LinkList<T> : IListDS<T> {private Node<T> head;public LinkList(){head = null;}public T this[int index]{get{return GetEle(index);}}public void Add(T item){Node<T> newNode = new Node<T>(item);if (head == null){head = newNode;}else{Node<T> temp = head;while (true){if (temp.Next != null){temp = temp.Next;}else{break;}}temp.Next = newNode;}}public void Clear(){head = null;}public T Delete(int index){T data = default(T);if (index == 0){data = head.Data;head = head.Next;}else{Node<T> temp = head;for (int i = 0; i < index - 1; i++){temp = temp.Next;}Node<T> preNode = temp;Node<T> currentNode = temp.Next;data = currentNode.Data;Node<T> nextNode = temp.Next.Next;preNode.Next = nextNode;}return data;}public T GetEle(int index){Node<T> temp = head;T data = temp.Data;if (index == 0){return data = temp.Data;}else{for (int i = 0; i < index; i++){temp = temp.Next;}data = temp.Data;}return data;}public int GetLength(){if (head == null) return 0;Node<T> temp = head;int count = 1;while (true){if (temp.Next != null){count++;temp = temp.Next;}else{break;}}return count;}public void Insert(T item, int index){Node<T> newNode = new Node<T>(item);if (index == 0){newNode.Next = head;head = newNode;}else{Node<T> temp = head;for (int i = 0; i < index - 1; i++){temp = temp.Next;}Node<T> preNode = temp;Node<T> currentNode = temp.Next;preNode.Next = newNode;newNode.Next = currentNode;}}public bool IsEmpty(){return head == null;}public int Locate(T value){Node<T> temp = head;if (temp == null){return -1;}else{int index = 0;while (true){if (temp.Data.Equals(value)){return index;}else{if (temp.Next != null){index++;temp = temp.Next;}else{break;}}}return -1;}} }

    雙向鏈表

    前面介紹的單鏈表允許從一個結點直接訪問它的后繼結點,所以, 找直接后繼結點的時間復雜度是 O(1)。但是,要找某個結點的直接前驅結點,只能從表的頭引用開始遍歷各結點。如果某個結點的 Next 等于該結點,那么,這個結點就是該結點的直接前驅結點。也就是說,找直接前驅結點的時間復雜度是 O(n), n是單鏈表的長度。當然,我們也可以在結點的引用域中保存直接前驅結點的地址而不是直接后繼結點的地址。這樣,找直接前驅結點的時間復雜度只有 O(1),但找直接后繼結點的時間復雜度是 O(n)。如果希望找直接前驅結點和直接后繼結點的時間復雜度都是 O(1),那么,需要在結點中設兩個引用域,一個保存直接前驅結點的地址,叫 prev,一個直接后繼結點的地址,叫 next,這樣的鏈表就是雙向鏈表(Doubly Linked List)。雙向鏈表的結點結構示意圖如圖所示。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UNn84VOH-1581864714522)(https://s1.ax1x.com/2018/12/26/Fg5Mhn.png)]

    雙向鏈表節點實現

    public class DbNode<T> {private T data; //數據域private DbNode<T> prev; //前驅引用域private DbNode<T> next; //后繼引用域//構造器public DbNode(T val, DbNode<T> p){data = val;next = p;}//構造器public DbNode(DbNode<T> p){next = p;}//構造器public DbNode(T val){data = val;next = null;}//構造器public DbNode(){data = default(T);next = null;}//數據域屬性public T Data{get { return data; }set { data = value; }}//前驅引用域屬性public DbNode<T> Prev{get { return prev; }set { prev = value; }}//后繼引用域屬性public DbNode<T> Next{get { return next; }set { next = value; }} }

    雙向鏈表插入示意圖

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yCTPlLtM-1581864714522)(https://s1.ax1x.com/2018/12/26/Fg5B1x.png)]

    循環鏈表

    有些應用不需要鏈表中有明顯的頭尾結點。在這種情況下,可能需要方便地從最后一個結點訪問到第一個結點。此時,最后一個結點的引用域不是空引用,而是保存的第一個結點的地址(如果該鏈表帶結點,則保存的是頭結點的地址),也就是頭引用的值。帶頭結點的循環鏈表(Circular Linked List)如圖所示。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Oihv4m5h-1581864714523)(https://s1.ax1x.com/2018/12/26/Fg52AH.png)]

    棧和隊列

    棧和隊列是非常重要的兩種數據結構,在軟件設計中應用很多。棧和隊列也是線性結構,線性表、棧和隊列這三種數據結構的數據元素以及數據元素間的邏輯關系完全相同,差別是線性表的操作不受限制,而棧和隊列的操作受到限制。
    棧的操作只能在表的一端進行,隊列的插入操作在表的一端進行而其它操作在表的另一端進行,所以,把棧和隊列稱為操作受限的線性表。

    棧(Stack)是操作限定在表的尾端進行的線性表。表尾由于要進行插入、刪除等操作,所以,它具有特殊的含義,把表尾稱為棧頂( Top),另一端是固定的,叫棧底( Bottom)。當棧中沒有數據元素時叫空棧(Empty Stack)。
    棧通常記為: S= (a1,a2,…,an),S是英文單詞stack的第 1 個字母。a1為棧底元素,an為棧頂元素。這n個數據元素按照a1,a2,…,an的順序依次入棧,而出棧的次序相反,an第一個出棧,a1最后一個出棧。所以,棧的操作是按照后進先出(Last In First Out,簡稱LIFO)或先進后出(First In Last Out,簡稱FILO)的原則進行的,因此,棧又稱為LIFO表或FILO表。棧的操作示意圖如圖所示。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-y4ib2xIf-1581864714523)(https://s1.ax1x.com/2018/12/26/FggUzD.png)]

    BCL中的棧

    C#2.0 一下版本只提供了非泛型的Stack類(存儲object類型)

    C#2.0 提供了泛型的Stack類

    重要的方法如下:

  • Push()入棧(添加數據)
  • Pop()出棧(刪除數據,返回被刪除的數據)
  • Peek()取得棧頂的數據,不刪除
  • Clear()清空所有數據
  • Count取得棧中數據的個數
  • 棧的接口定義

    public interface IStackDS<T> {int Count { get; }int GetLength(); //求棧的長度bool IsEmpty(); //判斷棧是否為空void Clear(); //清空操作void Push(T item); //入棧操作T Pop(); //出棧操作T Peek(); //取棧頂元素 }

    棧的存儲和代碼實現

    順序棧

    用一片連續的存儲空間來存儲棧中的數據元素(使用數組),這樣的棧稱為順序棧(Sequence Stack)。類似于順序表,用一維數組來存放順序棧中的數據元素。棧頂指示器 top 設在數組下標為 0 的端, top 隨著插入和刪除而變化,當棧為空時,top=-1。下圖是順序棧的棧頂指示器 top 與棧中數據元素的關系圖。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ngxwFMDC-1581864714524)(https://s1.ax1x.com/2018/12/26/FgRZgU.png)]

    class SeqStack<T> : IStackDS<T> {private T[] data;private int top;public SeqStack(int size){data = new T[size];top = -1;}public SeqStack() : this(10){}public int Count{get{return top + 1;}}public void Clear(){top = -1;}public int GetLength(){return Count;}public bool IsEmpty(){return Count == 0;}public T Peek(){return data[top];}public T Pop(){T temp = data[top];top--;return temp;}public void Push(T item){data[top + 1] = item;top++;} }

    鏈棧

    棧的另外一種存儲方式是鏈式存儲,這樣的棧稱為鏈棧(Linked Stack)。鏈棧通常用單鏈表來表示,它的實現是單鏈表的簡化。所以,鏈棧結點的結構與單鏈表結點的結構一樣。由于鏈棧的操作只是在一端進行,為了操作方便,把棧頂設在鏈表的頭部,并且不需要頭結點。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5nlAdDJY-1581864714524)(https://s1.ax1x.com/2018/12/26/FgRvI1.png)]

    鏈棧結點實現

    鏈棧結點代碼實現:

    class Node<T> {private T data;private Node<T> next;public Node(){this.data = default(T);this.next = null;}public Node(T data){this.data = data;this.next = null;}public Node(T value, Node<T> next){this.data = value;this.next = next;}public Node(Node<T> next){this.data = default(T);this.next = next;}public T Data{set { data = value; }get { return data; }}public Node<T> Next{set { next = value; }get { return next; }} }
    鏈棧代碼實現

    把鏈棧看作一個泛型類,類名為 LinkStack。 LinkStack類中有一個字段 top 表示棧頂指示器。由于棧只能訪問棧頂的數據元素,而鏈棧的棧頂指示器又不能指示棧的數據元素的個數。所以,求鏈棧的長度時,必須把棧中的數據元素一個個出棧,每出棧一個數據元素,計數器就增加 1,但這樣會破壞棧的結構。為保留棧中的數據元素,需把出棧的數據元素先壓入另外一個棧,計算完長度后,再把數據元素壓入原來的棧。但這種算法的空間復雜度和時間復雜度都很高,所以,以上兩種算法都不是理想的解決方法。理想的解決方法是 LinkStack類增設一個字段 num 表示鏈棧中結點的個數。

    class LinkStack<T> : IStackDS<T> {private Node<T> top;private int count = 0;public int Count{get{return count;}}public void Clear(){count = 0;top = null;}public int GetLength(){return count;}public bool IsEmpty(){return count == 0;}public T Peek(){return top.Data;}public T Pop(){T data = top.Data;top = top.Next;count--;return data;}public void Push(T item){Node<T> temp = new Node<T>(item);temp.Next = top;top = temp;count++;} }

    隊列

    隊列(Queue)是插入操作限定在表的尾部而其它操作限定在表的頭部進行的線性表。把進行插入操作的表尾稱為隊尾(Rear),把進行其它操作的頭部稱為隊頭(Front)。當隊列中沒有數據元素時稱為空隊列(Empty Queue)。
    隊列通常記為: Q= (a1,a2,…,an),Q是英文單詞queue的第 1 個字母。a1為隊頭元素,an為隊尾元素。這n個元素是按照a1,a2,…,an的次序依次入隊的,出對的次序與入隊相同,a1第一個出隊,an最后一個出隊。所以,對列的操作是按照先進先出(First In First Out)或后進后出( Last In Last Out)的原則進行的,因此,隊列又稱為FIFO表或LILO表。隊列Q的操作示意圖如圖所示。
    在實際生活中有許多類似于隊列的例子。比如,排隊取錢,先來的先取,后來的排在隊尾。
    隊列的操作是線性表操作的一個子集。隊列的操作主要包括在隊尾插入元素、在隊頭刪除元素、取隊頭元素和判斷隊列是否為空等。與棧一樣,隊列的運算是定義在邏輯結構層次上的,而運算的具體實現是建立在物理存儲結構層次上的。因此,把隊列的操作作為邏輯結構的一部分,每個操作的具體實現只有在確定了隊列的存儲結構之后才能完成。隊列的基本運算不是它的全部運算,而是一些常用的基本運算。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FX5KlOEk-1581864714524)(https://s1.ax1x.com/2018/12/26/FgfCYq.png)]

    BCL 中的隊列

    C#2.0 以下版本提供了非泛型的Queue類

    C#2.0 提供了泛型Queue類

    方法:

  • Enqueue()入隊(放在隊尾)
  • Dequeue()出隊(移除隊首元素,并返回被移除的元素)
  • Peek()取得隊首的元素,不移除
  • Clear()清空元素
    屬性
  • Count獲取隊列中元素的個數
  • 隊列接口定義

    interface IQueue<T> {int Count { get; }int GetLeng();bool IsEmpty();void Clear();void Enqueue(T item);T Dequeue();T Peek(); }

    隊列的存儲和代碼實現

    順序隊列

    用一片連續的存儲空間來存儲隊列中的數據元素,這樣的隊列稱為順序隊列(Sequence Queue)。類似于順序棧,用一維數組來存放順序隊列中的數據元素。隊頭位置設在數組下標為 0 的端,用 front 表示;隊尾位置設在數組的另一端,用 rear 表示。 front 和 rear 隨著插入和刪除而變化。當隊列為空時, front=rear=-1。
    圖是順序隊列的兩個指示器與隊列中數據元素的關系圖。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gGCIaKwp-1581864714525)(https://s1.ax1x.com/2018/12/26/FgfEXF.png)]

    class SeqQueue<T> : IQueue<T> {private T[] data;private int count;//數量private int rear;//隊尾private int front;//隊首public SeqQueue(int size){data = new T[size];count = 0;rear = front = -1;}public SeqQueue() : this(10){}public int Count{get{return count;}}public void Clear(){count = 0;rear = front = -1;}public T Dequeue(){if (count > 0){T temp = data[front + 1];front++;count--;return temp;}else{Console.WriteLine("隊列為空,無法取得隊首數據。");return default(T);}}public void Enqueue(T item){if (count == data.Length){Console.WriteLine("隊列已滿,不可以在添加數據");}else{if (rear == data.Length - 1){data[0] = item;rear = 0;count++;}else{data[rear + 1] = item;rear++;count++;}}}public int GetLeng(){return count;}public bool IsEmpty(){return count == 0;}public T Peek(){T temp = data[front + 1];return temp;} }
    循環順序隊列

    如果再有一個數據元素入隊就會出現溢出。但事實上隊列中并未滿,還有空閑空間,把這種現象稱為“假溢出”。這是由于隊列“隊尾入隊頭出”的操作原則造成的。解決假溢出的方法是將順序隊列看成是首尾相接的循環結構,頭尾指示器的關系不變,這種隊列叫循環順序隊列(Circular sequence Queue)。循環隊列如圖所示。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-GKnRoYFl-1581864714525)(https://s1.ax1x.com/2018/12/26/Fgfc7j.png)]

    把循環順序隊列看作是一個泛型類,類名叫 CSeqStack,“ C”是英文單詞 circular 的第 1 個字母。 CSeqStack類實現了接口 IQueue。用數組來存儲循環順序隊列中的元素,在 CSeqStack類中用字段 data 來表示。用字段maxsize 表示循環順序隊列的容量, maxsize 的值可以根據實際需要修改,這通過CSeqStack類的構造器中的參數 size 來實現,循環順序隊列中的元素由 data[0]開始依次順序存放。字段 front 表示隊頭, front 的范圍是 0 到 maxsize-1。字段 rear表示隊尾,rear 的范圍也是 0 到 maxsize-1。如果循環順序隊列為空,front=rear=-1。當執行入隊列操作時需要判斷循環順序隊列是否已滿,如果循環順序隊列已滿,(rear + 1) % maxsize==front , 循 環 順 序 隊 列 已 滿 不 能 插 入 元 素 。 所 以 ,CSeqStack類除了要實現接口 IQueue中的方法外,還需要實現判斷循環順序隊列是否已滿的成員方法。

    鏈隊列

    隊列的另外一種存儲方式是鏈式存儲,這樣的隊列稱為鏈隊列(Linked Queue)。同鏈棧一樣,鏈隊列通常用單鏈表來表示,它的實現是單鏈表的簡化。所以,鏈隊列的結點的結構與單鏈表一樣,如圖所示。由于鏈隊列的操作只是在一端進行,為了操作方便,把隊頭設在鏈表的頭部,并且不需要頭結點。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-x6cYEpNA-1581864714526)(https://s1.ax1x.com/2018/12/26/FgfqE9.png)]

    鏈隊列結點類
    class Node<T> {private T data;private Node<T> next;public Node(T data){this.data = data;}public T Data{set { data = value; }get { return data; }}public Node<T> Next{set { next = value; }get { return next; }} }
    鏈隊列代碼實現

    把鏈隊列看作一個泛型類,類名為 LinkQueue。 LinkQueue類中有兩個字段 front 和 rear,表示隊頭指示器和隊尾指示器。由于隊列只能訪問隊頭的數據元素,而鏈隊列的隊頭指示器和隊尾指示器又不能指示隊列的元素個數,所以,與鏈棧一樣,在 LinkQueue類增設一個字段 num 表示鏈隊列中結點的個數。

    class LinkQueue<T> : IQueue<T> {private Node<T> front;private Node<T> rear;private int count;public LinkQueue(){front = rear = null;count = 0;}public int Count{get { return count; }}public void Clear(){count = 0;rear = front = null;}public T Dequeue(){if (count == 0){Console.WriteLine("隊列為空,無法出隊");return default(T);}else if (count == 1){T temp = front.Data;front = rear = null;count = 0;return temp;}else{T temp = front.Data;front = front.Next;count--;return temp;}}public void Enqueue(T item){Node<T> temp = new Node<T>(item);if (count == 0){front = rear = temp;count = 1;}else{rear.Next = temp;rear = temp;count++;}}public int GetLeng(){return count;}public bool IsEmpty(){return count == 0;}public T Peek(){return front.Data;} }

    棧和隊列的應用舉例

    編程判斷一個字符串是否是回文。回文是指一個字符序列以中間字符為基準兩邊字符完全相同,如字符序列“ ACBDEDBCA”是回文。

    算法思想:判斷一個字符序列是否是回文,就是把第一個字符與最后一個字符相比較,第二個字符與倒數第二個字符比較,依次類推,第 i 個字符與第 n-i個字符比較。如果每次比較都相等,則為回文,如果某次比較不相等,就不是回文。因此,可以把字符序列分別入隊列和棧,然后逐個出隊列和出棧并比較出隊列的字符和出棧的字符是否相等,若全部相等則該字符序列就是回文,否則就不是回文。

    using System; using System.Collections.Generic;class Program {static void Main(string[] args){string str = Console.ReadLine();Stack<char> stack = new Stack<char>();Queue<char> queue = new Queue<char>();for (int i = 0; i < str.Length; i++){stack.Push(str[i]);queue.Enqueue(str[i]);}bool isHui = true;while (stack.Count > 0){if (stack.Pop() != queue.Dequeue()){isHui = false;break;}}Console.WriteLine("是否是回文字符串:" + isHui);Console.ReadKey();} }

    串和數組

    在應用程序中使用最頻繁的類型是字符串。字符串簡稱串,是一種特殊的線性表,其特殊性在于串中的數據元素是一個個的字符。字符串在計算機的許多方面應用很廣。如在匯編和高級語言的編譯程序中,源程序和目標程序都是字符串數據。在事務處理程序中,顧客的信息如姓名、地址等及貨物的名稱、產地和規格等,都被作為字符串來處理。另外,字符串還具有自身的一些特性。因此,把字符串作為一種數據結構來研究。

    串的基本概念

    串(String)由 n(n≥0)字符組成的有限序列。一般記為:
    S=”c1c2…cn” (n≥0)
    其中, S是串名,雙引號作為串的定界符,用雙引號引起來的字符序列是串值。 ci( 1≤i≤n)可以是字母、數字或其它字符, n為串的長度,當n=0 時,稱為空串(Empty String)。
    串中任意個連續的字符組成的子序列稱為該串的子串(Substring)。包含子串的串相應地稱為主串。子串的第一個字符在主串中的位置叫子串的位置。如串s1”abcdefg”,它的長度是 7,串s2”cdef”的長度是 4, s2是s1的子串, s2的位置是 3。
    如果兩個串的長度相等并且對應位置的字符都相等,則稱這兩個串相等。而在 C#中,比較兩個串是否相等還要看串的語言文化等信息。

    串的存儲和代碼實現

    由于串中的字符都是連續存儲的,而在 C#中串具有恒定不變的特性,即字符串一經創建,就不能將其變長、變短或者改變其中任何的字符。所以,這里不討論串的鏈式存儲,也不用接口來表示串的操作。同樣,把串看作是一個類,類名為 StringDS。取名為 StringDS 是為了和 C#自身的字符串類 String 相區別。類StringDS 只有一個字段,即存放串中字符序列的數組 data。由于串的運算有很多,類 StringDS 中只包含部分基本的運算。串類 StringDS中的方法和屬性:

    class StringDS {private char[] data;//用來存放字符串/// <summary>/// 構造器/// </summary>/// <param name="array">字符數組</param>public StringDS(char[] array){data = new char[array.Length];for (int i = 0; i < array.Length; i++){data[i] = array[i];}}/// <summary>/// 構造器/// </summary>/// <param name="str">字符串</param>public StringDS(string str){data = new char[str.Length];for (int i = 0; i < str.Length; i++){data[i] = str[i];}}/// <summary>/// 索引器/// </summary>/// <param name="index">索引下標</param>/// <returns></returns>public char this[int index]{get{return data[index];}}/// <summary>/// 獲得串的長度/// </summary>/// <returns>長度</returns>public int GetLength(){return data.Length;}/// <summary>/// 如果兩個字符串一樣長,返回0/// 如果當前字符串小于s,那么返回-1/// 如果當前字符串大于s,那么返回1/// </summary>/// <param name="">要比較的串</param>/// <returns></returns>public int Compare(StringDS s){int len = this.GetLength() > s.GetLength() ? this.GetLength() : s.GetLength(); //取得較短字符串;int index = -1;//用來記錄兩個字符串不相同字符的位置;for (int i = 0; i < len; i++){if (this[i] != s[i]){index = i;break;}}if (index != -1){if (this[index] > s[index]){return 1;}else{return -1;}}else{if (this.GetLength() == s.GetLength()){return 0;}else{if (this.GetLength() > s.GetLength()){return 1;}else{return -1;}}}}/// <summary>/// 剪切字符串/// </summary>/// <param name="index">剪切點的下標</param>/// <param name="length">要剪切的長度</param>/// <returns></returns>public StringDS SubString(int index, int length){char[] newData = new char[length];for (int i = index; i < index + length; i++){newData[i - index] = data[i];}return new StringDS(newData);}/// <summary>/// 拼接字符串/// </summary>/// <param name="s1">要拼接的串1</param>/// <param name="s2">要拼接的串2</param>/// <returns></returns>public static StringDS Concat(StringDS s1, StringDS s2){char[] newData = new char[s1.GetLength() + s2.GetLength()];for (int i = 0; i < s1.GetLength(); i++){newData[i] = s1[i];}for (int i = s1.GetLength(); i < s1.GetLength() + s2.GetLength(); i++){newData[i] = s2[i - s1.GetLength()];}return new StringDS(newData);}/// <summary>/// 查找當前串中與串s相同的第一個下標/// </summary>/// <param name="s">要在當前串中查找的串</param>/// <returns></returns>public int IndexOf(StringDS s){for (int i = 0; i <= this.GetLength() - s.GetLength(); i++){bool isEqual = true;for (int j = i; j < i + s.GetLength(); j++){if (this[j] != s[j - i]){isEqual = false;}}if (isEqual){return i;}else{continue;}}return -1;}/// <summary>/// 重寫ToString/// </summary>/// <returns>返回一個字符串</returns>public override string ToString(){return new string(data);} }

    C#中的串

    在 C#中,一個 String 表示一個恒定不變的字符序列集合。 String 類型是封閉類型,所以,它不能被其它類繼承,而它直接繼承自 object。因此, String 是引用類型,不是值類型,在托管堆上而不是在線程的堆棧上分配空間。 String 類型還 繼 承 了 IComparable 、 ICloneable 、 IConvertible 、 IComparable<string> 、IEnumerable<char>、 IEnumerable 和 IEquatable<string>等接口。 String 的恒定性指的是一個串一旦被創建,就不能將其變長、變短或者改變其中任何的字符。所以,當我們對一個串進行操作時,不能改變字符串,如在本書定義的 StringDS 類中,串連接、串插入和串刪除等操作的結果都是生成了新串而沒有改變原串。 C#也提供了 StringBuilder 類型來支持高效地動態創建字符串。
    在 C#中,創建串不能用 new 操作符,而是使用一種稱為字符串駐留的機制。

    這是因為 C#語言將 String 看作是基元類型。基元類型是被編譯器直接支持的類型,可以在源代碼中用文本常量(Literal)來直接表達字符串。當 C#編譯器對源代碼進行編譯時,將文本常量字符串存放在托管模塊的元數據中。而當 CLR 初始化時, CLR 創建一個空的散列表,其中的鍵是字符串,值為指向托管堆中字符串對象的引用。散列表就是哈希表。當 JIT編譯器編譯方法時,它會在散列表中查找每一個文本常量字符串。如果找不到,就會在托管堆中構造一個新的 String 對象(指向字符串),然后將該字符串和指向該字符串對象的引用添加到散列表中;如果找到了,不會執行任何操作。

    數組

    c#中的數組

    數組是一種常用的數據結構,可以看作是線性表的推廣。數組作為一種數據結構,其特點是結構中的數據元素可以是具有某種結構的數據,甚至可以是數組,但屬于同一數據類型。數組在許多高級語言里面都被作為固定類型來使用。
    數組是 n(n≥1)個相同數據類型的數據元素的有限序列。一維數組可以看作是一個線性表,二維數組可以看作是“數據元素是一維數組”的一維數組,三維數組可以看作是“數據元素是二維數組”的一維數組,依次類推。
    C#支持一維數組、多維數組及交錯數組(數組的數組)。所有的數組類型都隱含繼承自System.Array。Array 是一個抽象類,本身又繼承自 System.Object。所以,數組總是在托管堆上分配空間,是引用類型。任何數組變量包含的是一個指向數組的引用,而非數組本身。當數組中的元素的值類型時,該類型所需的內存空間也作為數組的一部分而分配;當數組的元素是引用類型時,數組包含是只是引用。

    Array類中的常用方法

    using System; using System.Collections; public abstract class Array : ICloneable, IList, ICollection, IEnumerable {//判斷 Array 是否具有固定大小。public bool IsFixedSize { get; }//獲取 Array 元素的個數。public int 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); }

    簡單排序方法

    排序

    排序(Sort)是計算機程序設計中的一種重要操作,也是日常生活中經常遇到的問題。例如,字典中的單詞是以字母的順序排列,否則,使用起來非常困難。同樣,存儲在計算機中的數據的次序,對于處理這些數據的算法的速度和簡便性而言,也具有非常深遠的意義。

    基本概念

    排序是把一個記錄(在排序中把數據元素稱為記錄)集合或序列重新排列成按記錄的某個數據項值遞增(或遞減)的序列。
    下表是一個學生成績表,其中某個學生記錄包括學號、姓名及計算機文化基礎、C 語言、數據結構等課程的成績和總成績等數據項。在排序時,如果用總成績來排序,則會得到一個有序序列;如果以數據結構成績進行排序,則會得到另一個有序序列。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-7CevmBS6-1581864714526)(https://s1.ax1x.com/2018/12/26/FgXCbq.png)]
    作為排序依據的數據項稱為“排序項”,也稱為記錄的關鍵碼(Keyword)。關鍵碼分為主關鍵碼(Primary Keyword)和次關鍵碼(Secondary Keyword)。一般地,若關鍵碼是主關鍵碼,則對于任意待排序的序列,經排序后得到的結果是唯一的;若關鍵碼是次關鍵碼,排序的結果不一定唯一,這是因為待排序的序列中可能存在具有相同關鍵碼值的記錄。此時,這些記錄在排序結果中,它們之間的位置關系與排序前不一定保持一致。如果使用某個排序方法對任意的記錄序列按關鍵碼進行排序,相同關鍵碼值的記錄之間的位置關系與排序前一致,則稱此排序方法是穩定的;如果不一致,則稱此排序方法是不穩定的。
    由于待排序的記錄的數量不同,使得排序過程中涉及的存儲器不同,可將排序方法分為內部排序(Internal Sorting)和外部排序(External Sorting)兩大類。
    內部排序指的是在排序的整個過程中,記錄全部存放在計算機的內存中,并且在內存中調整記錄之間的相對位置,在此期間沒有進行內、外存的數據交換。外部排序指的是在排序過程中,記錄的主要部分存放在外存中,借助于內存逐步調整記錄之間的相對位置。在這個過程中,需要不斷地在內、外存之間交換數據。

    直接插入排序

    插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,算法適用于少量數據的排序,時間復雜度為O(n^2)。是穩定的排序方法。插入算法把要排序的數組分成兩部分:第一部分包含了這個數組的所有元素,但將最后一個元素除外(讓數組多一個空間才有插入的位置),而第二部分就只包含這一個元素(即待插入元素)。在第一部分排序完成后,再將這個最后元素插入到已排好序的第一部分中。
    插入排序的基本思想是:每步將一個待排序的紀錄,按其關鍵碼值的大小插入前面已經排序的文件中適當位置上,直到全部插入完為止。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ufhWfSCk-1581864714527)(https://s1.ax1x.com/2018/12/26/FgX1IK.png)]

    using System;class Program {static void InsertSort(int[] dataArray){for (int i = 1; i < dataArray.Length; i++){int iValue = dataArray[i];bool isInsert = false;//拿到i位置的元素 跟前面所有的元素做比較//如果發現比i大的,就讓它向后移動for (int j = i - 1; j >= 0; j--){if (dataArray[j] > iValue){dataArray[j + 1] = dataArray[j];}else{//發現一個比i小的值就不移動了dataArray[j + 1] = iValue;isInsert = true;break;}}if (isInsert == false){dataArray[0] = iValue;}}}static void Main(string[] args){int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 };InsertSort(data);foreach (var temp in data){Console.Write(temp + " ");}cConsole.ReadKey();} }

    冒泡排序

    冒泡排序(Bubble Sort)的基本思想是:將相鄰的記錄的關鍵碼進行比較,若前面記錄的關鍵碼大于后面記錄的關鍵碼,則將它們交換,否則不交換。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-z8BCTzaR-1581864714527)(https://s1.ax1x.com/2018/12/26/FgXGGD.png)]

    class Program {static void Main(string[] args){int temp = 0;int[] arr = { 23, 44, 66, 76, 98, 11, 3, 9, 7 };#region 該段與排序無關Console.WriteLine("排序前的數組:");foreach (int item in arr){Console.Write(item + "");}Console.WriteLine();#endregionfor (int i = 0; i < arr.Length - 1; i++){#region 將大的數字移到數組的arr.Length-1-ifor (int j = 0; j < arr.Length - 1 - i; j++){if (arr[j] > arr[j + 1]){temp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = temp;}}#endregion}Console.WriteLine("排序后的數組:");foreach (int item in arr){Console.Write(item + "");}Console.WriteLine();Console.ReadKey();} }

    簡單選擇排序

    簡單選擇排序(Simple Select Sort)算法的基本思想是:從待排序的記錄序列中選擇關鍵碼最小(或最大)的記錄并將它與序列中的第一個記錄交換位置;然后從不包括第一個位置上的記錄序列中選擇關鍵碼最小(或最大)的記錄并將它與序列中的第二個記錄交換位置;如此重復,直到序列中只剩下一個記錄為止。
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oDcM6OiN-1581864714527)(https://s1.ax1x.com/2018/12/26/FgX8PO.png)]

    using System;class Program {static void SelectSort(int[] dataArray){for (int i = 0; i < dataArray.Length - 1; i++){int min = dataArray[i];int minIndex = i;//最小值所在索引for (int j = i + 1; j < dataArray.Length; j++){if (dataArray[j] < min){min = dataArray[j];minIndex = j;}}if (minIndex != i){int temp = dataArray[i];dataArray[i] = dataArray[minIndex];dataArray[minIndex] = temp;}}}static void Main(string[] args){int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 };SelectSort(data);foreach (var temp in data){Console.Write(temp + " ");}Console.ReadKey();} }

    快速排序

    快速排序由于排序效率綜合來說你幾種排序方法中效率較高,因此經常被采用,再加上快速排序思想----分治法也確實實用,因此很多軟件公司的筆試面試,包括像騰訊,微軟等知名IT公司都喜歡考這個,還有大大小的程序方面的考試如軟考,考研中也常常出現快速排序的身影。
    快速排序是C.R.A.Hoare于1962年提出的一種劃分交換排序。它采用了一種分治的策略,通常稱其為分治法(Divide-and-ConquerMethod)。

    該方法的基本思想是:

  • 先從數列中取出一個數作為基準數。
  • 分區過程,將比這個數大的數全放到它的右邊,小于或等于它的數全放到它的左邊。
  • 再對左右區間重復第二步,直到各區間只有一個數。
  • 快速排序詳細步驟

    以一個數組作為示例,取區間第一個數為基準數。
    初始時,i = 0; j = 9; X = a[i] = 72
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lqGdbCB6-1581864714528)(https://s1.ax1x.com/2018/12/26/FgjEwt.png)]
    由于已經將a[0]中的數保存到X中,可以理解成在數組a[0]上挖了個坑,可以將其它數據填充到這來。
    從j開始向前找一個比X小或等于X的數。當j=8,符合條件,將a[8]挖出再填到上一個坑a[0]中。a[0]=a[8]; i++; 這樣一個坑a[0]就被搞定了,但又形成了一個新坑a[8],這怎么辦了?簡單,再找數字來填a[8]這個坑。這次從i開始向后找一個大于X的數,當i=3,符合條件,將a[3]挖出再填到上一個坑中a[8]=a[3]; j–;
    數組變為:
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nkhzr6nQ-1581864714528)(https://s1.ax1x.com/2018/12/26/FgjAeI.png)]
    i = 3; j = 7; X=72
    再重復上面的步驟,先從后向前找,再從前向后找。
    從j開始向前找,當j=5,符合條件,將a[5]挖出填到上一個坑中,a[3] = a[5]; i++
    從i開始向后找,當i=5時,由于i==j退出。
    此時,i = j = 5,而a[5]剛好又是上次挖的坑,因此將X填入a[5]。
    數組變為:
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-H3fTJ27R-1581864714529)(https://s1.ax1x.com/2018/12/30/FhUB4S.png)]
    可以看出a[5]前面的數字都小于它,a[5]后面的數字都大于它。因此再對a[0…4]和a[6…9]這二個子區間重復上述步驟就可以了。

    快速排序代碼實現

    using System;class Program {/// <summary>/// 對數組dataArray中索引從left到right之間的數做排序/// </summary>/// <param name="dataArray">要排序的數組</param>/// <param name="left">要排序數據的開始索引</param>/// <param name="right">要排序數據的結束索引</param>static void QuickSort(int[] dataArray, int left, int right){if (left < right){int x = dataArray[left];//基準數, 把比它小或者等于它的 放在它的左邊,然后把比它大的放在它的右邊int i = left;int j = right;//用來做循環的標志位while (true && i < j)//當i==j的時候,說明我們找到了一個中間位置,這個中間位置就是基準數應該所在的位置 {//從后往前比較(從右向左比較) 找一個比x小(或者=)的數字,放在我們的坑里 坑位于i的位置while (true && i < j){if (dataArray[j] <= x) //找到了一個比基準數 小于或者等于的數子,應該把它放在x的左邊{dataArray[i] = dataArray[j];break;}else{j--;//向左移動 到下一個數字,然后做比較}}//從前往后(從左向右)找一個比x大的數字,放在我們的坑里面 現在的坑位于j的位置while (true && i < j){if (dataArray[i] > x){dataArray[j] = dataArray[i];break;}else{i++;}}}//跳出循環 現在i==j i是中間位置dataArray[i] = x;// left -i- rightQuickSort(dataArray, left, i - 1);QuickSort(dataArray, i + 1, right);}}static void Main(string[] args){int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 };QuickSort(data, 0, data.Length - 1);foreach (var temp in data){Console.Write(temp + " ");}Console.ReadKey();} }

    快排總結:

  • i =L; j = R; 將基準數挖出形成第一個坑a[i]。
  • j–由后向前找比它小的數,找到后挖出此數填前一個坑a[i]中。
  • i++由前向后找比它大的數,找到后也挖出此數填到前一個坑a[j]中。
  • 再重復執行2,3二步,直到i==j,將基準數填入a[i]中。
  • 總結

    以上是生活随笔為你收集整理的C#数据结构与算法总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: www.一区二区 | 男人亚洲天堂 | 国产一极片| 人人草人人澡 | 国产精品无码久久av | 一区二区视频在线观看免费 | 久久精品国产亚洲av无码娇色 | 午夜av在线播放 | 先锋av资源站 | 老司机午夜免费视频 | 成人黄色小说视频 | 久久国产柳州莫菁门 | 日日嗨av一区二区三区四区 | 国产第一页在线播放 | 性少妇mdms丰满hdfilm | 欧美一区二区三区四区在线观看 | 欧美日韩国产麻豆 | 美女吞精视频 | 一卡二卡三卡在线视频 | 91国偷自产中文字幕久久 | 亚洲人成网站999久久久综合 | 日韩女同互慰一区二区 | 国产成人中文字幕 | 亚洲午夜小视频 | 免费观看一区二区 | 国产对白自拍 | 日韩欧美精品一区 | 亚洲国内精品 | 国产在线综合视频 | 日本在线视频免费观看 | 98超碰在线 | aaaa毛片 | 中日韩黄色大片 | 超碰一区二区三区 | 欧美在线一区二区视频 | 性猛交富婆╳xxx乱大交天津 | 国产视频一二三区 | 久久精品国产亚洲av麻豆图片 | 亚洲午夜精品久久 | 欧美人与禽zozzozzo | 六月丁香婷婷综合 | 免费观看黄色网页 | 国产av剧情一区二区三区 | 成年人视频在线播放 | 国产免费av网址 | 亚洲性图视频 | 永久免费国产 | 最新毛片网 | 金瓶狂野欧美性猛交xxxx | 欧美在线a| 国产新婚疯狂做爰视频 | 黄色一极毛片 | 精品一区二区久久 | 农村老妇性真猛 | 国产激情免费视频 | www在线视频| 国产在线久 | 爱情岛论坛自拍亚洲品质极速最新章 | 性欧美巨大乳 | 四虎影酷| 91新网站| 欧美v日韩| 午夜粉色视频 | 中国二级毛片 | 91免费官网 | 国产一区欧美 | 农民人伦一区二区三区 | 在线免费日本 | 成人一级生活片 | 老色批永久免费网站www | 天天射综合网站 | 亚洲人成网址 | 成年人黄色 | 在线播放一区二区三区 | 天堂中文网 | 久久国产精品免费视频 | 奇米在线777 | 黄网在线观看视频 | 不卡日韩 | 一区二区三区国产精品视频 | 精品视频网 | 女人的洗澡毛片毛多 | 日本天堂网在线 | 综合在线播放 | 欧美黄色一级网站 | 欧美久久精品一级黑人c片 1000部多毛熟女毛茸茸 | 日韩亚洲国产精品 | 精品伦精品一区二区三区视频 | 大尺度做爰无遮挡露器官 | 美女午夜视频 | 韩国三色电费2024免费吗怎么看 | 久久久久青草 | 日本在线观看免费 | 一区视频 | 日韩免费影院 | 男女午夜爽爽 | 亚洲第一av | 日本精品不卡 | 欧美一级三级 |