【无标题】

目录

1 数组与矩阵

1.1 第一个只出现一次的字符位置

1.2 二维数组中的查找

1.3 数组中重复的数字

1.4 顺时针打印矩阵

1.5 替换空格

2 栈队列堆

2.1包含 min 函数的栈

2.2 数据流中的中位数

2.3 最小的 K 个数

2.4 用两个栈实现队列

2.5 栈的压入、弹出序列

2.6 字符流中第一个不重复的字符

2.7 滑动窗口的最大值

3 链表

3.1 从尾到头打印链表

3.2 合并两个有序链表

3.3 删除链表中重复的节点

3.4 反转链表

3.5 两个链表的第一个公共节点


1 数组与矩阵

1.1 第一个只出现一次的字符位置

1.2 二维数组中的查找

给定一个二维数组,其每一行从左到右递增排序,从上到下也是递增排序。给定一个数,判断这个数是否在该二维数组中。

解题思路:
       该二维数组中的一个数,小于它的数一定在其左边,大于它的数一定在其下边。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来快速地缩小查找区间,每次减少一行或者一列的元素。当前元素的查找区间为左下角的所有元素。

public static bool Find(int target,int[,] nums)
    {
        if (nums == null || nums.Length == 0 || nums.GetLength(0) == 0)
            return false;
        int row = nums.GetLength(1), col = nums.GetLength(0);
        int i = 0, j = col - 1;
        while (i <= row -1 && j >= 0){
            if (target == nums[i,j])
                return true;
            else if (target > nums[i,j])
                i++;
            else
                j--;
        }
        return false;
    }

1.3 数组中重复的数字

static public int duplicate(int[] nums)
    {
        for(int i = 0; i < nums.Length; i++)
        {
            while(nums[i] != i)
            {
                if (nums[i] == nums[nums[i]])
                {
                    return nums[i];
                }
                swap(nums, i, nums[i]);
            }
        }
        return -1;
    }

    static private void swap(int[] nums,int i,int j)
    {
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }

1.4 顺时针打印矩阵

按顺时针的方向,从外到里打印矩阵的值。
 * 下图的矩阵打印结果为:
 * 1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10

 解题思路:
 * 一层一层从外到里打印,观察可知每一层打印都有相同的处理步骤,
 * 唯一不同的是上下左右的边界不同了。
 * 因此使用四个变量 r1, r2, c1, c2 分别存储上下左右边界值,
 * 从而定义当前最外层。
 * 打印当前最外层的顺序:
 * 从左到右打印最上一行->从上到下打印最右一行
 * ->从右到左打印最下一行->从下到上打印最左一行。

public static void PrintInOrder(int[,] arr)
    {
        int c1 = 0, c2 = arr.GetLength(0)-1;
        int r1 = 0, r2 = arr.GetLength(1)-1;
        while (c1 <= c2 || r1 <= r2)
        {
            for (int j = c1; j < c2; j++)
                Print(arr[r1, j]);
            for (int i = r1; i < r2; i++)
                Print(arr[i, c2]);
            for (int j = c2; j >c1; j--)
                Print(arr[r2, j]);
            for (int i = r2; i >= r1+1; i--)
                Print(arr[i, c1]);
            c1++;c2--;r1++;r2--;
            if (c1 == c2 && r1 == r2)
                Console.WriteLine(arr[r1, c1]);
        }
    }

    private static void Print(int a)
    {
        Console.Write(a+",");
    }

1.5 替换空格

将一个字符串中的空格替换成 "%20"。

解题思路:

        数出原数组中出现了多少次空格,然后新声明一个数组,将其容量改为原数组容量加上空格数*3,再依次判断将原数组的元素和空格出现时要替换的字符存入新数组。

public static char[] ReplaceSpace(char[] arr1)
    {
        //记录原数组有多少个空格
        int count = 0;
        for (int n=0; n < arr1.Length; n++)
        {
            if(arr1[n] == ' ')
            {
                count++;
            }
        }
        //重新定义一个数组,用来存放替换空格后的结果
        char[] arr2=new char[arr1.Length+count*3];
        //i用来记录arr2下标
        //j用来记录arr1下标
        for (int i = 0, j = 0; i < arr2.Length && j<arr1.Length;i++,j++)
        {
            if(arr1[j] != ' ')//如果arr1此时的元素不是空格,则直接存入新的数组
            {
                arr2[i] = arr1[j];
            }
            else   
            {
                //遍历到arr1中空格的位置时
                //arr2从当前位置开始往后的依次存入%20
                arr2[i]='%';
                arr2[++i] = '2';
                arr2[++i] = '0';
            }
        }  
        return arr2;
    }

2 栈队列堆

2.1包含 min 函数的栈

实现一个包含 min() 函数的栈,该方法返回当前栈中最小的值。

解题思路
        使用一个额外的 minStack,栈顶元素为当前栈中最小的值。在对栈进行 push 入栈和 pop 出栈操作时,同样需要对 minStack 进行入栈出栈操作,从而使 minStack 栈顶元素一直为当前栈中最小的值。

        在进行 push 操作时,需要比较入栈元素和当前栈中最小值,将值较小的元素 push 到 minStack 中。

//定义一个类来实现要求的栈
public class MinStack
{
    Stack<int> data; //定义一个栈来存输入的数据
    Stack<int> min;  //定义一个栈来专门存放最小值

    public MinStack()
    {
        data = new Stack<int>();
        min = new Stack<int>();
    }

    public void Push(int value)//入栈
    {
        data.Push(value);

        //如果当前栈中没有元素,就默认入栈的第一个数字是最小值,存入min栈中
        //当前栈中有元素,则比较min栈中存的值是否大于等于入栈的值,是的话直接存入
        //多次的入栈操作只会让min栈的栈顶元素越来越小
        if(min.Count == 0||min.Peek()>=value)
            min.Push(value);
    }

    public void Pop() //出栈
    {
        if (data.Count == 0)
            return;
        
        //如果出栈的这个数字比min栈栈顶的值还小,则min栈也出栈
        //(一般只会等于不会大于
        int t=data.Pop();
        if(min.Peek()>=t)
            min.Pop();
    }
    public void minPeek() //检查总栈中的最小值
    {
        if (min.Count > 0)
            Console.WriteLine(min.Peek());
    }

2.2 数据流中的中位数

如何得到一个数据流中的中位数

解题思路

        对数组进行排序,再根据数组长度和奇偶取中位数。

        如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值;
        如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

public static void MidCount(int[] arr)
    {
        int[] arr2 = MaoPao(arr);//冒泡排序和插入排序都可以用
        float result = 0;

        float len = arr2.Length;
        if (len % 2 == 0)
            result = (len - 1) / 2 + len % 2;
        else
            result = arr2[arr2.Length / 2];
        Console.WriteLine(result);
    }
    //冒泡排序
    public static int[] MaoPao(int[] arr)
    {
        int[] arr2 = new int[arr.Length];
        for (int n = 0; n < arr.Length; n++)
        {
            arr2[n] = arr[n];
        }
        for (int i = 0; i < arr2.Length - 1; i++)
        {
            for (int j = 0; j < arr2.Length - 1 - i; j++)
            {
                if (arr2[j] > arr2[j + 1])
                {
                    int temp = arr2[j];
                    arr2[j] = arr2[j + 1];
                    arr2[j + 1] = temp;
                }
            }
        }
        return arr2;
    }
    //插入排序
    public static int[] ChaRu(int[] arr)
    {
        int[] arr2 = new int[arr.Length];
        for (int n = 0; n < arr.Length; n++)
        {
            arr2[n] = arr[n];
        }
        for (int i = 1; i < arr2.Length; i++)
        {
            int t = i;
            while (t - 1 >= 0 && arr2[t - 1] > arr2[t] )
            {
                int temp=arr2[t];
                arr2[t] = arr2[t - 1];
                arr2[t-1]=temp;
                t--;
            }
        }
        for (int n = 0; n < arr2.Length; n++)
        {
            Console.WriteLine(arr2[n]);
        }
            return arr2;
    }

2.3 最小的 K 个数

2.4 用两个栈实现队列

用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。

解题思路
        数据存放栈stack1用来处理入栈(push)操作,临时栈stack2用来处理出栈(pop)操作。一个元素进入stack1栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入stack2栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。

//定义一个类来完成两个栈实现队列
public class XQUeue
{
    private Stack<int> stack1;//数据存放栈
    private Stack<int> stack2;//数据输出时的临时栈

    public XQUeue()
    {
        stack1 = new Stack<int>();
        stack2 = new Stack<int>();
    }

    public void inQueue(int value)//入栈操作
    {
        stack1.Push(value);//直接压入数据存放栈
    }

    public void outQueue()//出栈操作
    {
        int value = 0;
        //为了达到队列的“先进先出”,把数据储存栈顺序反转,取出栈底数据
        while(stack1.Count > 0)
        {
            value = stack1.Pop();
            stack2.Push(value);//暂时放在临时栈
        }
        int t = stack2.Pop();
        Console.WriteLine(t);
        //再把栈反转一次,恢复原来的顺序
        while (stack2.Count > 0)
            stack1.Push(stack2.Pop());
    }

2.5 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。
eg:假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。

解题思路
    使用一个栈来模拟压入弹出操作。
    每次入栈一个元素后,都要判断一下栈顶元素是不是当前出栈序列popSequence 的第一个元素,如果是的话则执行出栈操作并将popSequence 往后移一位,继续进行判断。

public static bool popSequence(int[] arr1, int[] arr2)
    {
        //声明一个栈来模拟入栈操作
        Stack<int> stack = new Stack<int>();

        int t = 0;  //记录arr2的下标
        int count = 0;//记录栈中元素个数(用来判断是否栈空
        for (int i = 0; i < arr1.Length; i++)
        {
            int temp=arr1[i];
            stack.Push(temp);
            count++;
            while (count>0)
            {
                int value=stack.Peek();

                //如果栈顶元素和判断出栈数组的元素相等
                //则按照其进行出栈操作
                if (value == arr2[t])
                {
                    stack.Pop();
                    t++; //判断出栈数组的下一个元素
                    count--;
                }
                else
                    break;
            }
        }
        //如果循环完过后,判断出栈数组的元素已经全部遍历完,则是当前出栈顺序
        if (t == arr2.Length)
            return true;
        else
            return false;
    }

2.6 字符流中第一个不重复的字符

2.7 滑动窗口的最大值

3 链表

3.1 从尾到头打印链表

从尾到头反过来打印出每个结点的值。

解题思路:

栈具有后进先出的特点,在遍历链表时将值按顺序放入栈中,最后出栈的顺序即为逆序。

public class Node<T>//简答做一个单向链表
    {
        public T value;
        public Node<T> nextNode;
        public Node(T value)
        {
            this.value = value;
        }
    }

    public static void printListFromTailToHead(Node<int> head)
    {
        Stack<int> stack = new Stack<int>();
        //在遍历单向链表的同时,存入栈中
        while (head != null)
        {
            int v=head.value;
            stack.Push(v);
            head=head.nextNode;
        }
        //利用栈的先进先出的特点,达到反转打印的目的
        while(stack.Count > 0)
        {
            int t=stack.Pop();
            Console.Write(t);
        }
    }

3.2 合并两个有序链表

解题思路:

        直接迭代比较,谁更小就连上谁。

public static Node<int> Merge(Node<int> n1, Node<int> n2)
    {
        //创建头节点来返回结果链表
        Node<int> head=new Node<int>(-1);
        //创建一个临时指针来遍历链表
        Node<int> cur = head;
        //遍历链表
        while (n1 != null && n2 != null)
        {
            //循环比较,哪个链表当前值更小就连上哪边
            if (n1.value <= n2.value)
            {
                cur.nextNode = n1;
                n1 = n1.nextNode;
            }
            else
            {
                cur.nextNode = n2;
                n2 = n2.nextNode;
            }
            //临时指针移到已经确定的位置
            cur=cur.nextNode;
        }
        if (n1 != null)
            cur.nextNode = n1;
        if (n2 != null)
            cur.nextNode = n2;
        return head.nextNode;
    }

3.3 删除链表中重复的节点

在一个有序链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。例如链表 1->2->3->3->4->4->5 ,处理后为 1->2->5。

解题思路:

        迭代判断是否出现重复部分,如果没有就连上新的链表,有则直接跳过。

public static Node<int> RemoveRepeat(Node<int> n)
    {
        Node<int> head = new Node<int>(-1);
        Node<int> cur=head;
        int temp = 0;
        while (n != null && n.nextNode != null)
        {
            if (n.value != n.nextNode.value)
            {
                cur.nextNode = n;
                cur = cur.nextNode;
                n = n.nextNode;
                continue;
            }
            //如果没有到最后一个节点且当前节点的值与下一节点的相同
            while(n.nextNode!=null && n.value == n.nextNode.value)
            {
                temp=n.value;
                n = n.nextNode;
                if (n.nextNode != null && n.nextNode.value == temp  )
                    continue;
                else
                {
                    n = n.nextNode;
                    cur.nextNode=n;//跳过前面重复的节点
                    break;
                }
            }
        }
        return head.nextNode;
    }

3.4 反转链表

给定一个单链表的头结点pHead(该头节点值是1),长度为n,反转该链表后,返回新链表的表头。如当输入链表{1,2,3}时,经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。

解题思路
    栈具有后进先出的特点,在遍历链表时将值按顺序放入栈中,最后出栈的顺序即为逆序。

public static Node<int> ReverseList(Node<int> n)
    {
        Node<int> cur = n;
        Stack<int> stack=new Stack<int>();
        //利用栈先进先出的特点来反转存放链表值
        while(cur!= null)
        {
            stack.Push(cur.value);
            cur=cur.nextNode;
        }
        //声明一个新的链表来存放反转后的值
        Node<int> newNode=new Node<int>(-1);
        cur=newNode;
        int temp = 0;
        while (stack.Count != 0)
        {
            temp=stack.Pop();
            Node<int> tNode = new Node<int>(temp);
            cur.nextNode = tNode;
            cur=cur.nextNode;
        }
        return newNode.nextNode;
    }

3.5 两个链表的第一个公共节点

如图,找到两个链表的第一个公共节点c1。

解题思路
    设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。

        当访问链表 A 的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问链表 B 的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。

public static Node<int> FindFirstCommonNode(Node<int> a, Node<int> b)
    {
        //创建两个临时指针来分别遍历两个链表
        Node<int> p1 = a;
        Node<int> p2 = b;
        int count = 0; //用来记录完整遍历了几次
        while (p1 != p2)
        {
            //如果两个链表都遍历过一遍但是仍没找到公共节点
            //就说明这两个链表没有公共节点
            if (p1.value == p2.value||count>2)
                break;
            else
            {
                p1 = p1.nextNode;
                p2 = p2.nextNode;
            }

            //如果哪边的链表已经遍历完了,就从另一个表头重新开始遍历
            if (p1 == null)
            {
                p1 = b;
                count++;
            }
            else if (p2 == null)
            {
                p2 = a;
                count++;
            }
        }
        if (count > 2)//没有公共节点的情况
        {
            Console.WriteLine("-1");
            return null;
        }
        else
            return p2;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值