In my Android app, I am trying to sort Bus route tags in order 1, 2, 3..etc.
For that I am using this
Collections.sort(directions, Comparator { lhs, rhs ->
var obj1 = lhs.short_names.firstOrNull() ?: ""
var obj2 = rhs.short_names.firstOrNull() ?: ""
if (obj1 === obj2) {
obj1 = lhs.headsigns.firstOrNull() ?: ""
obj2 = rhs.headsigns.firstOrNull() ?: ""
if (obj1 === obj2) {
return#Comparator 0
}
obj1.compareTo(obj2)
} else {
obj1.compareTo(obj2)
}
The issue I am having is this sorts them, but will run into the issue of
1, 2, 3, 30, 31, 4, 5
How should I change this to get the correct ordering.
If you need just a simple number comparison you can do it like that.
directions.sortWith(Comparator { lhs, rhs ->
val i1 = lhs.toInt()
val i2 = rhs.toInt()
when {
i1 < i2 -> -1
i1 > i2 -> 1
else -> 0
}
})
As hotkey pointed out the code above can be replaced with almost identical implementation that looks much simplier.
directions.sortBy { it.toInt() }
The general version of this algorithm is called alphanum sorting and described in details here. I made a Kotlin port of this algorithm, which you can use. It's more complicated than what you need, but it will solve your problem.
class AlphanumComparator : Comparator<String> {
override fun compare(s1: String, s2: String): Int {
var thisMarker = 0
var thatMarker = 0
val s1Length = s1.length
val s2Length = s2.length
while (thisMarker < s1Length && thatMarker < s2Length) {
val thisChunk = getChunk(s1, s1Length, thisMarker)
thisMarker += thisChunk.length
val thatChunk = getChunk(s2, s2Length, thatMarker)
thatMarker += thatChunk.length
// If both chunks contain numeric characters, sort them numerically.
var result: Int
if (isDigit(thisChunk[0]) && isDigit(thatChunk[0])) {
// Simple chunk comparison by length.
val thisChunkLength = thisChunk.length
result = thisChunkLength - thatChunk.length
// If equal, the first different number counts.
if (result == 0) {
for (i in 0..thisChunkLength - 1) {
result = thisChunk[i] - thatChunk[i]
if (result != 0) {
return result
}
}
}
} else {
result = thisChunk.compareTo(thatChunk)
}
if (result != 0) {
return result
}
}
return s1Length - s2Length
}
private fun getChunk(string: String, length: Int, marker: Int): String {
var current = marker
val chunk = StringBuilder()
var c = string[current]
chunk.append(c)
current++
if (isDigit(c)) {
while (current < length) {
c = string[current]
if (!isDigit(c)) {
break
}
chunk.append(c)
current++
}
} else {
while (current < length) {
c = string[current]
if (isDigit(c)) {
break
}
chunk.append(c)
current++
}
}
return chunk.toString()
}
private fun isDigit(ch: Char): Boolean {
return '0' <= ch && ch <= '9'
}
}
To use this Comparator just call
directions.sortWith(AlphanumComparator())
If you don't need it to be coded in Kotlin you can just take an original Java version on Dave Koelle's page. And the Kotlin version of the algorithm can be also found on GitHub.
Related
I am attempting the following problem:
Two players start with a pile of coins, and each player has the choice of removing either one or two coins from the pile. The player who removes the last coin loses.
I have come up with the following naive, recursive implementation
(playgound):
func gameWinner(coinsRemaining int, currentPlayer string) string {
if coinsRemaining <= 0 {
return currentPlayer
}
var nextPlayer string
if currentPlayer == "you" {
nextPlayer = "them"
} else {
nextPlayer = "you"
}
if gameWinner(coinsRemaining-1, nextPlayer) == currentPlayer || gameWinner(coinsRemaining-2, nextPlayer) == currentPlayer {
return currentPlayer
} else {
return nextPlayer
}
}
func main() {
fmt.Println(gameWinner(4, "you")) // "them"
}
The above code works fine.
However, when I improve this solution by implementing memoization (see below, or on the playgound), I get the wrong answer.
func gameWinner(coinsRemaining int, currentPlayer string, memo map[int]string) string {
if coinsRemaining <= 0 {
return currentPlayer
}
var nextPlayer string
if currentPlayer == "you" {
nextPlayer = "them"
} else {
nextPlayer = "you"
}
if _, exists := memo[coinsRemaining]; !exists {
if gameWinner(coinsRemaining-1, nextPlayer, memo) == currentPlayer || gameWinner(coinsRemaining-2, nextPlayer, memo) == currentPlayer {
memo[coinsRemaining] = currentPlayer
} else {
memo[coinsRemaining] = nextPlayer
}
}
return memo[coinsRemaining]
}
func main() {
memo := make(map[int]string)
fmt.Println(gameWinner(4, "you", memo))
}
Any help as to why the second implementation is returning different values to the first would be greatly appreciated!
Your memoization is wrong: the winner does not only depend on the current number of coins, but also on whose turn it is. You need something like the following:
type state struct {
coinsRemaining int
currentPlayer string
}
memo := make(map[state]string)
I converted my Java files to Kotlin. And I fixed most of them. However, I don't understand this part using Comparator.
wordItems.sortWith(Comparator { (_, word), (_, word) ->
val size1 = word!!.length
val size2 = word.length
if (size1 < size2) {
return#Collections.sort - 1
} else if (size1 == size2) {
return#Collections.sort 0
} else {
return#Collections.sort 1
}
})
And this one also, I don't understand.
Collections.sort(wordItems) { (_, word), (_, word) ->
val size1 = word!!.length
val size2 = word.length
if (size1 < size2) {
return#Collections.sort - 1
} else if (size1 == size2) {
return#Collections.sort 0
} else {
return#Collections.sort 1
}
}
How can I change this to make it work?
Comparator is a SAM (single abstract method) interface in Java. An implementation of such interface can be instantiated in Kotlin using SAM-conversion technique, so instead of writing an anonymous object implementing Comparator like this:
val comparator = object : Comparator<WordItem> {
override fun compare(item1: WordItem, item2: WordItem): Int {
val size1 = item1.word.length
val size2 = item2.word.length
...
}
}
you can write
val comparator = Comparator<WordItem> { item1, item2 ->
val size1 = item1.word.length
val size2 = item2.word.length
...
}
Here I assume that each WordItem has the word property.
It seems that you're comparing items by word length. In that case you can simplify such comparator even more with compareBy function:
wordItems.sortWith(compareBy { it.word.length })
or sortBy function
wordItems.sortBy { it.word.length }
Any suggestions on how to improve the following code to make it more Functional Programming oriented. Specifically how to remove the MutableList which signifies historical states. There are two data classes: Bank, which represents a riverbank (number of missionaries and number of cannibals currently on the bank) and BankState which represents a historical state of the two banks (the source bank, target bank and boatAtSource - a boolean which indicates whether the boat is currently at the source or target bank). overloaded operator function plus adds missionaries and cannibals to a riverbank and function minus removes them from a riverbank. The boat function is the one which carries the most heft. You can call the following algorithm from fun main (app.kt) as such:
app.kt
fun main(args:Array<String>) {
val source:Bank = Bank(3,3)
val target:Bank = Bank()
source boat target
}
Bank.kt
data class Bank(val missionaries:Int=0,val cannibals:Int=0)
data class BankState(val sourceTarget:Pair<Bank,Bank>,val boatAtSource:Boolean)
operator fun Bank.plus(b:Pair<Int,Int>):Bank = Bank(this.missionaries+b.first,this.cannibals+b.second)
operator fun Bank.minus(b:Pair<Int,Int>):Bank = Bank(this.missionaries-b.first,this.cannibals-b.second)
infix fun Bank.boat(target:Bank):List<BankState> {
val begin = Pair(this,target)
val history = mutableListOf<BankState>(BankState(begin,true))
boat(begin,true,this.missionaries,this.cannibals,history)
return history
}
fun boat(sourceTarget:Pair<Bank,Bank>,
boatAtSource:Boolean,
totalMissionaries:Int,
totalCannibals:Int,
history:MutableList<BankState>):Boolean {
if(sourceTarget.first.cannibals+sourceTarget.second.cannibals==totalCannibals &&
sourceTarget.first.missionaries + sourceTarget.second.missionaries==totalMissionaries &&
sourceTarget.first.cannibals>=0 &&
sourceTarget.first.missionaries>=0 &&
sourceTarget.second.cannibals>=0 &&
sourceTarget.second.missionaries>=0 &&
(sourceTarget.first.missionaries==0 || sourceTarget.first.missionaries>=sourceTarget.first.cannibals) &&
(sourceTarget.second.missionaries==0 || sourceTarget.second.missionaries >= sourceTarget.second.cannibals)) {
if(sourceTarget.second.missionaries==totalMissionaries &&
sourceTarget.second.cannibals==totalCannibals) {
history.forEach(::println)
return true
} else {
val deltas = listOf(Pair(0,1),Pair(1,1),Pair(1,0),Pair(2,0),Pair(0,2))
val comparator = object : Comparator<Pair<Pair<Boolean,Int>,Pair<Bank,Bank>>> {
override fun compare(arg1:Pair<Pair<Boolean,Int>,Pair<Bank,Bank>>,arg2:Pair<Pair<Boolean,Int>,Pair<Bank,Bank>>):Int {
if(arg1.first.first && arg2.first.first) {
return if(arg1.first.second<arg2.first.second) -1 else if(arg1.first.second>arg2.first.second) 1 else 0
} else if(arg1.first.first){
return 1
} else if(arg2.first.first) {
return -1
}
return 0
}
}
val result = deltas.map{
checkNext(it.first,it.second,totalMissionaries,totalCannibals,history,sourceTarget,boatAtSource)
}.maxWith(comparator)
if(result?.first?.first!=null && result.first.first) {
history.add(BankState(result.second,!boatAtSource))
return true;
}
}
}
return false
}
fun checkNext(missionariesDelta:Int,
cannibalsDelta:Int,
totalMissionaries:Int,
totalCannibals:Int,
history:MutableList<BankState>,
sourceTarget:Pair<Bank,Bank>,
boatAtSource:Boolean):Pair<Pair<Boolean,Int>,Pair<Bank,Bank>> {
val nextSrcTgt = if(boatAtSource) Pair(sourceTarget.first-Pair(missionariesDelta,cannibalsDelta),sourceTarget.second+Pair(missionariesDelta,cannibalsDelta))
else Pair(sourceTarget.first+Pair(missionariesDelta,cannibalsDelta),sourceTarget.second-Pair(missionariesDelta,cannibalsDelta))
val bankState:BankState = BankState(nextSrcTgt,!boatAtSource)
if(!history.contains(bankState)) {
history.add(bankState)
val combo2:Boolean = boat(nextSrcTgt,!boatAtSource,totalMissionaries,totalCannibals,history)
val combo2Depth = history.size
history.remove(bankState)
return Pair(Pair(combo2,combo2Depth),nextSrcTgt)
} else {
return Pair(Pair(false,0),nextSrcTgt)
}
}
Given an array of integers nums and an integer k. Find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the difference between i and j is at most k.
It is supposed to give me true, but it gives me false.
Any help, I appreciate it. Thank you so much.
class Solution
{
func containsNearbyDuplicate (nums: [Int], _ k: Int) -> Bool
{
var dict = [Int:Int]()
for i in 0..<nums.count
{
if dict[nums[i]] != nil
{
if dict.values.contains(nums[i]) && (i - dict[nums[i]]! <= k)
{
return true
}
else
{
dict[i] = nums[i]
}
}
}
return false
}
}
let test1 = Solution()
//var haha = [1,2,1,5,6,7,6,8,7,5]
//var haha = [1]
//var haha = [1,2]
//var haha = [1,2,3,5,6,8]
var haha = [-1,-1]
var result = test1.containsNearbyDuplicate(haha,1)
print(result)
You never add anything to dict:
func containsNearbyDuplicate (nums: [Int], _ k: Int) ->Bool
{
var dict = [Int:Int]()
for i in 0..<nums.count
{
if dict[nums[i]] != nil // This prevents anything to be added to dict
{
if dict.values.contains(nums[i]) && (i - dict[nums[i]]! <= k)
{
return true
}
else
{
dict[i] = nums[i] // This is never executed because of the above if above
}
}
}
return false
}
Try this one:
class Solution {
func containsNearbyDuplicate (nums: [Int], _ k: Int) ->Bool {
var dict = [Int:Int]()
for i in 0..<nums.count {
if let firstIndex = dict[nums[i]] where i - firstIndex <= k {
return true
}
dict[nums[i]] = i
}
return false
}
}
Consider a MxN bitmap where the cells are 0 or 1. '1' means filled and '0' means empty.
Find the number of 'holes' in the bitmap, where a hole is a contiguous region of empty cells.
For example, this has two holes:
11111
10101
10101
11111
... and this has only one:
11111
10001
10101
11111
What is the fastest way, when M and N are both between 1 and 8?
Clarification: diagonals are not considered contiguous, only side-adjacency matters.
Note: I am looking for something that takes advantage of the data format. I know how to transform this into a graph and [BD]FS it but that seems overkill.
You need to do connected component labeling on your image. You can use the Two-pass algorithm described in the Wikipedia article I linked above. Given the small size of your problem, the One-pass algorithm may suffice.
You could also use BFS/DFS but I'd recommend the above algorithms.
This seems like a nice use of the disjoint-set data structure.
Convert the bitmap to a 2d array
loop through each element
if the current element is a 0, merge it with the set of one its 'previous' empty neighbors (already visited)
if it has no empty neighbors, add it to its own set
then just count the number of sets
There may be advantages gained by using table lookups and bitwise operations.
For example whole line of 8 pixels may be looked up in 256 element table, so number of holes in a field 1xN is got by single lookup. Then there may be some lookup table of 256xK elements, where K is number of hole configurations in previous line, contatining number of complete holes and next hole configuration. That's just an idea.
I wrote an article describe the answer on Medium https://medium.com/#ahmed.wael888/bitmap-holes-count-using-typescript-javascript-387b51dd754a
but here is the code, the logic isn't complicated and you can understand it without reading the article.
export class CountBitMapHoles {
bitMapArr: number[][];
holesArr: Hole[] = [];
maxRows: number;
maxCols: number;
constructor(bitMapArr: string[] | number[][]) {
if (typeof bitMapArr[0] == 'string') {
this.bitMapArr = (bitMapArr as string[]).map(
(word: string): number[] => word.split('').map((bit: string): number => +bit))
} else {
this.bitMapArr = bitMapArr as number[][]
}
this.maxRows = this.bitMapArr.length;
this.maxCols = this.bitMapArr[0].length;
}
moveToDirection(direction: Direction, currentPosition: number[]) {
switch (direction) {
case Direction.up:
return [currentPosition[0] - 1, currentPosition[1]]
case Direction.down:
return [currentPosition[0] + 1, currentPosition[1]]
case Direction.right:
return [currentPosition[0], currentPosition[1] + 1]
case Direction.left:
return [currentPosition[0], currentPosition[1] - 1]
}
}
reverseDirection(direction: Direction) {
switch (direction) {
case Direction.up:
return Direction.down;
case Direction.down:
return Direction.up
case Direction.right:
return Direction.left
case Direction.left:
return Direction.right
}
}
findNeighbor(parentDir: Direction, currentPosition: number[]) {
let directions: Direction[] = []
if (parentDir === Direction.root) {
directions = this.returnAvailableDirections(currentPosition);
} else {
this.holesArr[this.holesArr.length - 1].positions.push(currentPosition)
directions = this.returnAvailableDirections(currentPosition).filter((direction) => direction != parentDir);
}
directions.forEach((direction) => {
const childPosition = this.moveToDirection(direction, currentPosition)
if (this.bitMapArr[childPosition[0]][childPosition[1]] === 0 && !this.checkIfCurrentPositionExist(childPosition)) {
this.findNeighbor(this.reverseDirection(direction), childPosition)
}
});
return
}
returnAvailableDirections(currentPosition: number[]): Direction[] {
if (currentPosition[0] == 0 && currentPosition[1] == 0) {
return [Direction.right, Direction.down]
} else if (currentPosition[0] == 0 && currentPosition[1] == this.maxCols - 1) {
return [Direction.down, Direction.left]
} else if (currentPosition[0] == this.maxRows - 1 && currentPosition[1] == this.maxCols - 1) {
return [Direction.left, Direction.up]
} else if (currentPosition[0] == this.maxRows - 1 && currentPosition[1] == 0) {
return [Direction.up, Direction.right]
} else if (currentPosition[1] == this.maxCols - 1) {
return [Direction.down, Direction.left, Direction.up]
} else if (currentPosition[0] == this.maxRows - 1) {
return [Direction.left, Direction.up, Direction.right]
} else if (currentPosition[1] == 0) {
return [Direction.up, Direction.right, Direction.down]
} else if (currentPosition[0] == 0) {
return [Direction.right, Direction.down, Direction.left]
} else {
return [Direction.right, Direction.down, Direction.left, Direction.up]
}
}
checkIfCurrentPositionExist(currentPosition: number[]): boolean {
let found = false;
return this.holesArr.some((hole) => {
const foundPosition = hole.positions.find(
(position) => (position[0] == currentPosition[0] && position[1] == currentPosition[1]));
if (foundPosition) {
found = true;
}
return found;
})
}
exec() {
this.bitMapArr.forEach((row, rowIndex) => {
row.forEach((bit, colIndex) => {
if (bit === 0) {
const currentPosition = [rowIndex, colIndex];
if (!this.checkIfCurrentPositionExist(currentPosition)) {
this.holesArr.push({
holeNumber: this.holesArr.length + 1,
positions: [currentPosition]
});
this.findNeighbor(Direction.root, currentPosition);
}
}
});
});
console.log(this.holesArr.length)
this.holesArr.forEach(hole => {
console.log(hole.positions)
});
return this.holesArr.length
}
}
enum Direction {
up = 'up',
down = 'down',
right = 'right',
left = 'left',
root = 'root'
}
interface Hole {
holeNumber: number;
positions: number[][]
}
main.ts file
import {CountBitMapHoles} from './bitmap-holes'
const line = ['1010111', '1001011', '0001101', '1111001', '0101011']
function main() {
const countBitMapHoles = new CountBitMapHoles(line)
countBitMapHoles.exec()
}
main()
function BitmapHoles(strArr) {
let returnArry = [];
let indexOfZ = [];
let subarr;
for(let i=0 ; i < strArr.length; i++){
subarr = strArr[i].split("");
let index = [];
for(let y=0 ; y < subarr.length; y++){
if(subarr[y] == 0)
index.push(y);
if(y == subarr.length-1)
indexOfZ.push(index);
}
}
for(let i=0 ; i < indexOfZ.length; i++){
for(let j=0; j<indexOfZ[i].length ; j++){
if(indexOfZ[i+1] && (indexOfZ[i][j]==indexOfZ[i+1][j] || indexOfZ[i+1].indexOf(indexOfZ[i][j])))
returnArry.indexOf(strArr[i]) < 0 ? returnArry.push(strArr[i]): false;
if(Math.abs(indexOfZ[i][j]-indexOfZ[i][j+1])==1)
returnArry.indexOf(strArr[i]) < 0 ? returnArry.push(strArr[i]): false;
}
}
return returnArry.length;
}
// keep this function call here
console.log(BitmapHoles(readline()));
function findHoles(map) {
let hole = 0;
const isHole = (i, j) => map[i] && map[i][j] === 0;
for (let i = 0; i < map.length; i++) {
for (let j = 0; j < map[i].length; j++) {
if (isHole(i, j)) {
markHole(i, j);
hole++;
}
}
}
function markHole(i, j) {
if (isHole(i, j)) {
map[i][j] = 2;
markHole(i, j - 1);
markHole(i, j + 1);
markHole(i + 1, j);
markHole(i - 1, j);
}
}
return hole;
}