LeetCode 178周赛 首刷的奇妙经历

博主参加了LeetCode的第一场周赛,遭遇了从C语言到Java的转换挑战。在解决T1-T4题目时,遇到代码提交规则、内存管理和多关键字排序等问题。尽管过程中遇到了错误,但通过调整思路和语言,最终完成了比赛,反思了自己的不足并鼓励自己继续学习和进步。

emm,上个周日参加的LeetCode周赛,发生了一些神奇的事情。

首先,这场比赛是我在LeetCode参加的第一场,确切的说,这是我第一次在LeetCode上刷题(雾)。我也不知道我哪里来的兴致,在看到比赛时上头的直接报名。。。。。。

当然这样的第一次确实在比赛中造成了困扰。。

题目中相应代码会贴出来,只能过,不保证高名次

T1:有多少小于当前数字的数字

打开题目一看,哇塞,眼前一亮,这难道就是传说中的防爆零题目吗?操起键盘就要开写。我兴奋又熟练的打开我的DEV C++,心想着竞赛要用最快的语言来写代码,java它是真的慢,C语言数据大的时候能快出一个数量级呢/doge。

当我开开心心敲出一个带有头文件和主函数以及自己定义的函数的代码准备上交时,我打开提交窗口。。。

woc?什么鬼,为什么这个东西疯狂报错。。。。

冷静分析发现,事情好像并不是我想象的那样。。emm,好滴吧,看来我并不知道这个网站的代码提交规则,我只是,按着别的网站规则相当然的敲了二十多行代码。

接着,我打开了网站内的说明,这才搞懂原来它的编程目的是实现相关功能的函数或是类方法,并不需要提供主函数和头文件。emm。。。。看来是时候做出进步了。

在拷贝了它提供给我的函数框架后,我望着代码,陷入了沉思。。。我发现好像在学校学了个假的C语言,由于并没有对于内存管理的相关知识,我尝试着用malloc乱搞了几下过后,便放弃了。看了一眼时间,天!快半个小时过去了。。。。

“那好吧”,我默默关掉了Dev编译器,打开了eclipse,将提交语言切换成了java。啊~nice,java的画风果然清晰了好多。数组直接当做对象传来传去,不用像C语言那样同内存和指针斗智斗勇了,可以将注意力放到程序实现上,只需要在类中实现特定方法即可,爽的一匹。

然后,顺着之前的思路,三下五除二,重写了便代码,把这个送分题水掉。难忘的第一题结束了,来不及庆祝,因为比赛还在如火如荼进行着。

T1代码

class Solution {
    public int[] smallerNumbersThanCurrent(int[] nums) {
        int[] ans = new int[nums.length];
        for(int i = 0;i < nums.length;i++)
        {
        	for(int j = 0;j < nums.length;j++)
        	{
        		ans[i] += nums[j] < nums[i] ? 1 : 0;
        	}
        }
        return ans;
    }
}

T2:通过投票对团队排名

确定语言以后,我很快进入到了比赛的状态。因为一直在其他的oj上用java刷题,所以用java实现算法的熟练度其实并不亚于C语言,反而由于java提供了很多封装好的类,让很多算法的实现难度大大减小。比如这一道题。

其实这一道题难点就在于多关键字排序和名称映射。能把出现的名称映射成唯一确定的id,投票后根据票号进行多关键字排序,这个题目也就解决了

幸而,java的面向对象特性能让我很方便的建立“团队”这个类并实现Comparable接口重写compareTo方法实现多关键字排序。(其实题目中对于优先级的描述补充了全相等比较名称的规则,在实现程序的时候才发现)在类中,定义这个团队的名称(char类型),获得票数的统计数组及其对应的id。

另外,java的map也相当的给力,处理这种名称到id非常得心应手。另外由题知,所有名称一定会在第一次的投票中出现,所以,就先来造一波映射,后面慢慢用。

最后,java对字符串的处理也很方便。最终结果,直接用加号就可以实现字符串和单个字符的拼接,可谓是么得感情的答案产生机器。、

手起码落,第一次提交死在了空字符串和空指针的错误上。忘记了空答案应该是空字符串而非空指针,于是改了发答案的初值,提交,完事。 罚了五分钟,还可以接受。

T2代码:

class Solution {
    class Team implements Comparable<Team>
	{
		int[] p = new int[30];
		char ch;
		
		Team(char ch)
		{
			this.ch = ch;
		}
		
		@Override
		public int compareTo(Team o) {
			// TODO Auto-generated method stub
			for(int i = 0;i < tot;i++)
			{
				if(p[i] != o.p[i])
				{
					return o.p[i] - p[i];
				}
			}
			return ch - o.ch;
		}
		
	}
	
	HashMap<Character,Integer> map = new HashMap();
	int tot;
	
	public String rankTeams(String[] votes) {
		String ans = "";
		Team[] team = new Team[30];
		
		for(int i = 0;i < votes[0].length();i++)
		{
			team[tot] = new Team(votes[0].charAt(i));
			map.put(votes[0].charAt(i), tot);
			tot++;
		}
		String s;
		for(int i = 0;i < votes.length;i++)
		{
			s = votes[i];
			for(int j = 0;j < s.length();j++)
			{
				team[map.get(s.charAt(j))].p[j]++;
			}
		}
		Arrays.sort(team,0,tot);
		for(int i = 0;i < tot;i++)
		{
			ans += team[i].ch;
		}
		return ans;
    }
}

T3:二叉树中的列表

读完题,发现是个匹配,还是树上的链匹配,心中各种几乎忘掉的自动机知识在翻腾,又转念想自动机好似不是处理这样的问题的,应该是某种转移,接着,胡思乱想时看到了数据范围。。。。。。

emm。。模拟走起,时不待我,上手干活!!

光速复制代码开始实现。不一会,我把题目中的方法写成了递归求解,进行匹配。样例过了以后开开心心提交。。。WA。。。。。发现原来起点不必是二叉树跟而可以是每个节点。。。。于是又罚了五分钟 (这个故事告诉我们,要好好审题!!!!!)

于是重写方法,把这个递归验证写道另一个方法里,这个方法改成递归枚举每个节调用验证方法递归验证。(没有任何优化,好在数据小)

这一次,成功的过了,舒了一口气,看看时间还有二十多分钟,最后一道题。我来了

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
   private boolean check(ListNode head, TreeNode root)
	{
		if(head == null)
    	{
    		return true;
    	}
    	if(root == null || (root != null && root.val != head.val))
    	{
    		return false;
    	}
    	return check(head.next,root.left) || check(head.next,root.right);
	}
    
    public boolean isSubPath(ListNode head, TreeNode root) {
    	if(root == null)
    	{
    		return false;
    	}
    	if(check(head,root))
    	{
    		return true;
    	}
    	return isSubPath(head,root.left) || isSubPath(head,root.right);
    }
}

T4:使网格图至少有一条有效路径的最小代价

一路过关斩将,来到最后一关。看到困难的题签激起了我的斗志。不过吸取了上题的教训,我努力而又认真的读完了题目。

第一想法是动态规划,转移到达每个格子的最小代价。顺着这个思路尝试着列了下转移方程,发现。。。emm这东西根本无法搞,因为行走的方向不是固定的,是会有反复的。至少对于一个格子来说,他可能从相邻的四个格子的任意一个中转移过来。而这样的转移不能保证之前状态的有效,也不能保证转移的无后效性。所以,放弃。

接着,考虑到格子之间可以相互走,想到这东西具有图的性质,一个节点向相邻的四个节点连边代表可行走,连接着格子指向的点的边行走代价应该是0,其余边为1。整张图就建好了,剩下的就是求答案了。

但是不知道是我哪根筋抽了,对于这张图求答案的第一反应竟然是网络流。。。好在 基本上快要想不起来的最大流算法让我放弃了这个想法,转向了最短路径。。。

模型建完,手玩了两组数据,基本上没什么问题,上手实现。当代码敲完的时候,已经剩下不到十分钟了,我着急忙慌得补了一个主方法进行测试。可是答案却迟迟不肯出来。于是,,,奇怪的现象出现了!:

我瞎调,我错误;我错误,我焦虑;我焦虑,我瞎调;
我瞎调,我错误;我错误,我焦虑;我焦虑,我瞎调;
我瞎调,我错误;我错误,我焦虑;我焦虑,我瞎调;
。。。。。。。。

就这样,七分钟一闪而过,我望着已经结束的比赛,看着排除了绝大部分错误的代码,有一丝丝失望。

抱着侥幸心理,重整了下思路,打印了一下最短路径的答案数组一康。。。我挺萌的只是算错了重点的下标,问题根本不在算法上。

那好滴吧,返回答案的下标减一,我得到了期盼许久的答案。

也终于。。在比赛结束的三分钟后,我拿下了最后一道题。。。(心情复杂:MMP最后一题没有算上分)

T4代码:

class Solution {
    class V implements Comparable<V>{
		V(int p,int val,int next)
		{
			this.p = p;
			this.val = val;
			this.next = next;
		}
		V(int p,int val)
		{
			this.p = p;
			this.val = val;
		}
		int p;
		int val;
		int next;
		@Override
		public int compareTo(V o) {
			// TODO Auto-generated method stub
			return val - o.val;
		}
	}
	V[] b = new V[100000];
	int n,m;
	int tot = 0;
	int[] dis = new int[10050];
	int[] head = new int[10050];
	int[][] step = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
	boolean[] c = new boolean[10050];
	PriorityQueue<V> queue = new PriorityQueue();
	
	private int calc(int y,int x)
	{
		if(y < 0 || x < 0 || y >= n || x >= m)
		{
			return -1;
		}
		return y * m + x;
	}
	
	public int minCost(int[][] grid) {
		n = grid.length;
		m = grid[0].length;
		int nn = n * m;
		for(int i = 0;i < n;i++)
		{
			for(int j = 0,k,kk;j < m;j++)
			{
				k = calc(i,j);
				for(int x = 0;x < 4;x++)
				{
					kk = calc(i + step[x][0],j + step[x][1]);
					if(kk == -1)
					{
						continue;
					}
					b[++tot] = new V(kk,x + 1 == grid[i][j] ? 0 : 1,head[k]);
					head[k] = tot;
				}
			}
		}
		for(int i = 0;i < nn;i++)
		{
			dis[i] = 1 << 30;
		}
		dis[0] = 0;
		queue.add(new V(0,0));
		V v;
		while(!queue.isEmpty())
		{
			v = queue.poll();
			if(c[v.p])
			{
				continue;
			}
			c[v.p] = true;
			for(int i = head[v.p],k;i != 0;i = b[i].next)
			{
				k = b[i].p;
				if(c[k])
				{
					continue;
				}
				if(dis[v.p] + b[i].val < dis[k])
				{
					dis[k] = dis[v.p] + b[i].val;
					queue.add(new V(k,dis[k]));
				}
			}
		}
		return dis[nn - 1];
    }
}

这次比赛排名在300左右,有幸运也有遗憾,比赛就这样结束了,但是学习的路还远远没有结束。不论是不熟悉内存管理的C语言,还是图论模型的错误使用,这些都是可以看到的,有待进步的地方。好在在这里,比赛不是目的,练习代码,学习知识,差缺补漏才是目的。写博客复盘也带有此意。

那么,今后的周赛,加油,奥利给!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值