CodeWars-Directions Reduction

Avicii

Wake Me Up - Avicii / Aloe Blacc

鸽子王走了

一.题目

今天题目挺长的

Once upon a time, on a way through the old wild west,…

… a man was given directions to go from one point to another. The directions were "NORTH", "SOUTH", "WEST", "EAST". Clearly "NORTH" and "SOUTH" are opposite, "WEST" and "EAST" too. Going to one direction and coming back the opposite direction is a needless effort. Since this is the wild west, with dreadfull weather and not much water, it's important to save yourself some energy, otherwise you might die of thirst!
How I crossed the desert the smart way.

The directions given to the man are, for example, the following:

["NORTH", "SOUTH", "SOUTH", "EAST", "WEST", "NORTH", "WEST"].

or

{ "NORTH", "SOUTH", "SOUTH", "EAST", "WEST", "NORTH", "WEST" };

or (haskell)

[North, South, South, East, West, North, West]

You can immediatly see that going "NORTH" and then "SOUTH" is not reasonable, better stay to the same place! So the task is to give to the man a simplified version of the plan. A better plan in this case is simply:

["WEST"]

or

{ "WEST" }

or (haskell)

[West]

or (rust)

[WEST];

Other examples:

In ["NORTH", "SOUTH", "EAST", "WEST"], the direction "NORTH" + "SOUTH" is going north and coming back right away. What a waste of time! Better to do nothing.

The path becomes ["EAST", "WEST"], now "EAST" and "WEST" annihilate each other, therefore, the final result is [] (nil in Clojure).

In ["NORTH", "EAST", "WEST", "SOUTH", "WEST", "WEST"], "NORTH" and "SOUTH" are not directly opposite but they become directly opposite after the reduction of "EAST" and "WEST" so the whole path is reducible to ["WEST", "WEST"].
Task

Write a function dirReduc which will take an array of strings and returns an array of strings with the needless directions removed (W<->E or S<->N side by side).

The Haskell version takes a list of directions with data Direction = North | East | West | South. The Clojure version returns nil when the path is reduced to nothing. The Rust version takes a slice of enum Direction {NORTH, SOUTH, EAST, WEST}.
Examples

dirReduc(@[@"NORTH", @"SOUTH", @"SOUTH", @"EAST", @"WEST", @"NORTH", @"WEST"]); // => @[@"WEST"]
dirReduc(@[@"NORTH", @"SOUTH", @"SOUTH", @"EAST", @"WEST", @"NORTH"]); // => @[]

See more examples in "Example Tests"
Note

Not all paths can be made simpler. The path ["NORTH", "WEST", "SOUTH", "EAST"] is not reducible. "NORTH" and "WEST", "WEST" and "SOUTH", "SOUTH" and "EAST" are not directly opposite of each other and can't become such. Hence the result path is itself : ["NORTH", "WEST", "SOUTH", "EAST"].

大致意思就是

一个男人在荒漠中迷失了,现在他得到一条指令前往下一个地点,只有东南西北四个方向,很明显,如果往东走后再往西走是没有必要的。这只会浪费他的体力,而他的体力有限,必须尽量缩少移动的步数,所以请写一个函数,传入列表,里面是东南西北四个方向。判断有哪些可以不必走的指令(走东后走西,走西后走东,走南后走北,走北后走南),删除这些制定,最后返回最简指令。不过如果指令是[北,西,南,东]没有可以缩减的指令,因为东和西,南和北没有直接相连,所以没有可以缩减的指令,最后返回它自己。

初始代码

def dirReduc(arr):

举个栗子

a = ["NORTH", "SOUTH", "SOUTH", "EAST", "WEST", "NORTH", "WEST"]
test.assert_equals(dirReduc(a), ['WEST'])
u=["NORTH", "WEST", "SOUTH", "EAST"]
test.assert_equals(dirReduc(u), ["NORTH", "WEST", "SOUTH", "EAST"])

2.思路和代码

思路

最开始还一位是简单的判断南多少个,北多少个,然后直接互相抵消。但仔细看最后的说明,题目说了,如果不是直接相连的相反方向的指令是不能消除的,这也是为什么

["NORTH", "WEST", "SOUTH", "EAST"]

并没有缩减指令的原因。

这时想起了学数据结构时候学的一个概念——栈(stack)

栈又称先进后出表,也可以叫后进先出表。栈包含两个部分,栈顶和栈底。但这里并不详细的讲栈,而是讲讲栈的思想
栈的思想是这样的,如果我要存入3个字符A、B和C,这时候按顺序A先,然后是B,最后是C进入。

| (空)
'A' |
'A' ,'B'|
'A' ,'B', 'C' |

如果要删除B的话,只能把C删除,再删除B,不能直接删除B

'A' ,'B', 'C' |
'A' ,'B'|
'A' |

后进先出的思想就体现在这里,先进入的元素(A),进入栈底。我们增加元素和删除元素的操作,都只能在尾端也就是栈顶操作。如果要操作的元素并不在栈顶,也就是最尾端,那就不能对其操作。只能对尾端的元素操作,也就是后进来的元素操作,这就是后进先出。

所以在这里我们构造一个空列表,我命名为stack。
然后遍历一次传进来的指令,按顺序把方向指令一个一个进入,并判断尾端两个是否是相反的指令,是就删除,不是就不做任何操作。
例如stack最后两个指令如果是南和北,那么最后两位赋值为空,即删除。然后下一个方向进来,判断最后两个是否相等。

代码

def dirReduc(arr):
    stack=[]
    for direction in arr:
        stack.append(direction)
        while set(stack[-2:])==set(['NORTH','SOUTH']) or set(stack[-2:])==set(['WEST','EAST']):
            stack[-2:]=[]
    return stack

代码解析

命名一个stack空列表,然后做遍历,每个元素依次放入我们的栈stack中,如果最后两位相等则删除。遍历结束,返回栈。
这里判断相等的方法我用的是集合,set()函数将列表后两位变为集合,与我们的目标集合判断是否相等。

set(stack[-2:])==set(['NORTH','SOUTH'])
set(stack[-2:])==set(['WEST','EAST'])

不过当时写的时候脑子秀逗了,用了while,现在都忘了当时为什么这么写,┑( ̄Д  ̄)┍

while set(stack[-2:])==set(['NORTH','SOUTH']) or set(stack[-2:])==set(['WEST','EAST']):

其实使用if判断即可

if set(stack[-2:])==set(['NORTH','SOUTH']) or set(stack[-2:])==set(['WEST','EAST']):

三.最优解代码

Best Practices

代码如下

opposite = {'NORTH': 'SOUTH', 'EAST': 'WEST', 'SOUTH': 'NORTH', 'WEST': 'EAST'}

def dirReduc(plan):
    new_plan = []
    for d in plan:
        if new_plan and new_plan[-1] == opposite[d]:
            new_plan.pop()
        else:
            new_plan.append(d)
    return new_plan

嗯,看得出这才是栈的最正确用法。。。233红红火火恍恍惚惚🤣

准确来说,栈对元素的操作是最后一个,而我的代码是对尾端两个元素的操作,只能说是我的代码是借了栈的思想,但并不是栈。
另外在这里,我们可以看到他判断方向相反的方法是字典,字典是一种映射关系。冒号 (:)左边是键,右边是值(key-value即键值对)。四个方向都有自己相反方向的映射。举个例子,如果列表非空,最后一个元素是南,下一个方向进来了——北。通过字典我们查到北的相反方向是南,与列表最后一位相等,删除列表最后的元素南。

假设 此时列表非空,最后一个元素是南
new_plan[-1]='南'
d = '北'
opposite[d]=‘南’
new_plan[-1]==opposite[d] 是true
new_plan.pop() 删除最后一个元素

讲完第一的答案我们来说说第二的答案,第二的答案可以说非常的有意思。 我很喜欢这种新奇的思路。

def dirReduc(arr):
    dir = " ".join(arr)
    dir2 = dir.replace("NORTH SOUTH",'').replace("SOUTH NORTH",'').replace("EAST WEST",'').replace("WEST EAST",'')
    dir3 = dir2.split()
    return dirReduc(dir3) if len(dir3) < len(arr) else dir3

给我一个列表
我把他们列表每个元素用空格连接,变成了字符串
我用字符串的方法,去掉了相邻的相反的指令
不过缩短一次可不够,新生成的短指令可能还存在可以缩短的指令哦
那就来个递归吧,如果这次缩短了,我的长度变短,那就再对这个列表进行缩短指令的函数,如果这次长度没有变短,说明已经是最简指令了,返回最简指令。

为什么用递归,
用题目中的那个例子举例

["NORTH", "SOUTH", "SOUTH", "EAST", "WEST", "NORTH", "WEST"]

如果只做一次,dir3将得到

["SOUTH", "NORTH", "WEST"]

这里还能再缩短一次,因为原本SOUTHNORTH之间的EASTWEST去除了,现在SOUTHNORTH连接在一起应该去除的但指令就做到这了,所以用递归的方法。

四.总结

1.相反方向的判断
我的方法是用集合,第一的答案是用字典(映射的思想)。

2.栈的思想
一个列表对最后一个元素的地方进行添加和删除。(后进先出)

3.递归
递归可以视为一种循环,递归很厉害,但我对递归的掌握还不是很熟悉。

一点题外话

起床看到的第一条消息就是Avicii死亡的消息,当时只觉得在开玩笑吗。但是当确认是真的时候,很震撼又很难受。
Without You 这首歌已经忘了听了几遍了。真是好听....

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容